#include #include #include #include #include #include #include #include #include "GtermP.h" /* * Gterm -- Graphics terminal widget. This widget implements only the * window specific graphics output and graphics window input functions. * Protocol translation (e.g. Tek emulation) and i/o is done elsewhere; * see for example gtermio.c. */ #define DefaultAlphaFont 3 #define DefaultDialogFont 3 #define DefaultMarkerTextFont 3 #define ZOOM_TOL 0.0001 static Dimension defXDim = DEF_WIDTH; static Dimension defYDim = DEF_HEIGHT; /* Default translations for Gterm window. */ /* Omitted for now: Ctrl ~Meta : popup-menu(tekMenu) */ static char defaultGtermTranslations[] = "\ : m_create() \n\ : crosshair(on) \n\ : crosshair(on) \n\ : crosshair(off) \n\ : enter-window() \n\ : leave-window() \n\ : graphics-input() \n\ : track-cursor() \n\ "; /* Default translations when pointer is over a marker. */ static char defaultMarkerTranslations[] = "\ !Shift : m_rotateResize() \n\ : m_moveResize() \n\ !Shift : m_raise() m_markpos() \n\ : m_raise() m_markposAdd() \n\ : m_redraw() m_destroyNull() \n\ : m_lower() \n\ BackSpace: m_deleteDestroy() \n\ Delete: m_deleteDestroy() \n\ : m_input() \n\ : track-cursor() \n\ "; static XtResource resources[] = { {XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension), XtOffset(Widget,core.width), XtRDimension, (caddr_t)&defXDim}, {XtNheight, XtCHeight, XtRDimension, sizeof(Dimension), XtOffset(Widget,core.height), XtRDimension, (caddr_t)&defYDim}, {XtNalphaFont1, XtCFont, XtRFontStruct, sizeof(XFontStruct *), XtOffset(GtermWidget,gterm.alphaFont1), XtRString, "nil2"}, {XtNalphaFont2, XtCFont, XtRFontStruct, sizeof(XFontStruct *), XtOffset(GtermWidget,gterm.alphaFont2), XtRString, "5x8"}, {XtNalphaFont3, XtCFont, XtRFontStruct, sizeof(XFontStruct *), XtOffset(GtermWidget,gterm.alphaFont3), XtRString, "6x10"}, {XtNalphaFont4, XtCFont, XtRFontStruct, sizeof(XFontStruct *), XtOffset(GtermWidget,gterm.alphaFont4), XtRString, "7x13"}, {XtNalphaFont5, XtCFont, XtRFontStruct, sizeof(XFontStruct *), XtOffset(GtermWidget,gterm.alphaFont5), XtRString, "8x13"}, {XtNalphaFont6, XtCFont, XtRFontStruct, sizeof(XFontStruct *), XtOffset(GtermWidget,gterm.alphaFont6), XtRString, "9x15"}, {XtNalphaFont7, XtCFont, XtRFontStruct, sizeof(XFontStruct *), XtOffset(GtermWidget,gterm.alphaFont7), XtRString, "9x15"}, {XtNalphaFont8, XtCFont, XtRFontStruct, sizeof(XFontStruct *), XtOffset(GtermWidget,gterm.alphaFont8), XtRString, "9x15"}, {XtNdialogFont1, XtCFont, XtRFontStruct, sizeof(XFontStruct *), XtOffset(GtermWidget,gterm.dialogFont1), XtRString, "nil2"}, {XtNdialogFont2, XtCFont, XtRFontStruct, sizeof(XFontStruct *), XtOffset(GtermWidget,gterm.dialogFont2), XtRString, "5x8"}, {XtNdialogFont3, XtCFont, XtRFontStruct, sizeof(XFontStruct *), XtOffset(GtermWidget,gterm.dialogFont3), XtRString, "6x13"}, {XtNdialogFont4, XtCFont, XtRFontStruct, sizeof(XFontStruct *), XtOffset(GtermWidget,gterm.dialogFont4), XtRString, "7x13"}, {XtNdialogFont5, XtCFont, XtRFontStruct, sizeof(XFontStruct *), XtOffset(GtermWidget,gterm.dialogFont5), XtRString, "8x13"}, {XtNdialogFont6, XtCFont, XtRFontStruct, sizeof(XFontStruct *), XtOffset(GtermWidget,gterm.dialogFont6), XtRString, "9x15"}, {XtNdialogFont7, XtCFont, XtRFontStruct, sizeof(XFontStruct *), XtOffset(GtermWidget,gterm.dialogFont7), XtRString, "9x15"}, {XtNdialogFont8, XtCFont, XtRFontStruct, sizeof(XFontStruct *), XtOffset(GtermWidget,gterm.dialogFont8), XtRString, "9x15"}, {XtNdialogBgColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.dialogBgColor), XtRString, "yellow"}, {XtNdialogFgColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.dialogFgColor), XtRString, "black"}, {XtNidleCursorBgColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.idleCursorBgColor), XtRString, "white"}, {XtNidleCursorFgColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.idleCursorFgColor), XtRString, "black"}, {XtNbusyCursorBgColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.busyCursorBgColor), XtRString, "white"}, {XtNbusyCursorFgColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.busyCursorFgColor), XtRString, "black"}, {XtNginmodeCursorBgColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.ginmodeCursorBgColor), XtRString, "black"}, {XtNginmodeCursorFgColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.ginmodeCursorFgColor), XtRString, "white"}, {XtNginmodeBlinkInterval, XtCInt, XtRInt, sizeof(int), XtOffset(GtermWidget,gterm.ginmodeBlinkInterval), XtRImmediate, 0}, {XtNcrosshairCursorColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.crosshairCursorColor), XtRString, "red"}, {XtNidleCursor, XtCString, XtRString, sizeof(String), XtOffset(GtermWidget,gterm.idleCursor), XtRString, "plus"}, {XtNbusyCursor, XtCString, XtRString, sizeof(String), XtOffset(GtermWidget,gterm.busyCursor), XtRString, "watch"}, {XtNginmodeCursor, XtCString, XtRString, sizeof(String), XtOffset(GtermWidget,gterm.ginmodeCursor), XtRString, "full_crosshair"}, {XtNwarpCursor, XtCBoolean, XtRBoolean, sizeof(Boolean), XtOffset(GtermWidget,gterm.warpCursor), XtRImmediate, (caddr_t)DEF_WARPCURSOR}, {XtNraiseWindow, XtCBoolean, XtRBoolean, sizeof(Boolean), XtOffset(GtermWidget,gterm.raiseWindow), XtRImmediate, (caddr_t)DEF_RAISEWINDOW}, {XtNdeiconifyWindow, XtCBoolean, XtRBoolean, sizeof(Boolean), XtOffset(GtermWidget,gterm.deiconifyWindow), XtRImmediate, (caddr_t)DEF_DEICONIFYWINDOW}, {XtNuseTimers, XtCBoolean, XtRBoolean, sizeof(Boolean), XtOffset(GtermWidget,gterm.useTimers), XtRImmediate, (caddr_t)DEF_USETIMERS}, {XtNcolor0, XtCBackground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.color0), XtRString, "black"}, {XtNcolor1, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.color1), XtRString, "white"}, {XtNcolor2, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.color2), XtRString, "red"}, {XtNcolor3, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.color3), XtRString, "green"}, {XtNcolor4, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.color4), XtRString, "blue"}, {XtNcolor5, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.color5), XtRString, "cyan"}, {XtNcolor6, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.color6), XtRString, "yellow"}, {XtNcolor7, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.color7), XtRString, "magenta"}, {XtNcolor8, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.color8), XtRString, "purple"}, {XtNcolor9, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.color9), XtRString, "darkslategray"}, {XtNcopyOnResize, XtCBoolean, XtRBoolean, sizeof(Boolean), XtOffset(GtermWidget,gterm.copyOnResize), XtRImmediate, (caddr_t)DEF_COPYONRESIZE}, {XtNcmapName, XtCString, XtRString, sizeof(String), XtOffset(GtermWidget,gterm.cmapName), XtRImmediate, (caddr_t)"default"}, {XtNcmapInitialize, XtCBoolean, XtRBoolean, sizeof(Boolean), XtOffset(GtermWidget,gterm.cmapInitialize), XtRImmediate, (caddr_t)FALSE}, {XtNbasePixel, XtCInt, XtRInt, sizeof(int), XtOffset(GtermWidget,gterm.base_pixel), XtRImmediate, (caddr_t)DEF_BASEPIXEL}, {XtNcmapUpdate, XtCInt, XtRInt, sizeof(int), XtOffset(GtermWidget,gterm.cmapUpdate), XtRImmediate, (caddr_t)DEF_CMAPUPDATE}, {XtNcmapShadow, XtCInt, XtRInt, sizeof(int), XtOffset(GtermWidget,gterm.cmapShadow), XtRImmediate, (caddr_t)DEF_CMAPSHADOW}, {XtNcmapInterpolate, XtCBoolean, XtRBoolean, sizeof(Boolean), XtOffset(GtermWidget,gterm.cmapInterpolate), XtRImmediate, (caddr_t)True}, {XtNcacheRasters, XtCString, XtRString, sizeof(String), XtOffset(GtermWidget,gterm.cacheRasters), XtRImmediate, (caddr_t)"whenNeeded"}, {XtNmaxRasters, XtCInt, XtRInt, sizeof(int), XtOffset(GtermWidget,gterm.maxRasters), XtRImmediate, (caddr_t)MAX_RASTERS}, {XtNmaxMappings, XtCInt, XtRInt, sizeof(int), XtOffset(GtermWidget,gterm.maxMappings), XtRImmediate, (caddr_t)MAX_MAPPINGS}, {XtNmaxColors, XtCInt, XtRInt, sizeof(int), XtOffset(GtermWidget,gterm.maxColors), XtRImmediate, (caddr_t)DEF_MAXCOLORS}, {XtNmarkerTranslations, XtCString, XtRString, sizeof(String), XtOffset(GtermWidget,gterm.gm_translations), XtRImmediate, (caddr_t)defaultMarkerTranslations}, {XtNdefaultMarker, XtCString, XtRString, sizeof(String), XtOffset(GtermWidget,gterm.gm_defaultMarker), XtRImmediate, (caddr_t)"rectangle"}, {XtNnearEdge, XtCInt, XtRInt, sizeof(int), XtOffset(GtermWidget,gterm.gm_nearEdge), XtRImmediate, (caddr_t)E_DIST}, {XtNnearVertex, XtCInt, XtRInt, sizeof(int), XtOffset(GtermWidget,gterm.gm_nearVertex), XtRImmediate, (caddr_t)V_DIST}, {XtNmarkerLineWidth, XtCInt, XtRInt, sizeof(int), XtOffset(GtermWidget,gterm.gm_lineWidth), XtRImmediate, (caddr_t)1}, {XtNmarkerLineStyle, XtCInt, XtRInt, sizeof(int), XtOffset(GtermWidget,gterm.gm_lineStyle), XtRImmediate, (caddr_t)LineSolid}, {XtNmarkerFill, XtCBoolean, XtRBoolean, sizeof(Boolean), XtOffset(GtermWidget,gterm.gm_fill), XtRImmediate, (caddr_t)False}, {XtNmarkerFillColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.gm_fillColor), XtRString, "SlateGray"}, {XtNmarkerFillBgColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.gm_fillBgColor), XtRString, "black"}, {XtNmarkerFillStyle, XtCInt, XtRInt, sizeof(int), XtOffset(GtermWidget,gterm.gm_fillStyle), XtRImmediate, (caddr_t)FillSolid}, {XtNxorFill, XtCBoolean, XtRBoolean, sizeof(Boolean), XtOffset(GtermWidget,gterm.gm_xorFill), XtRImmediate, (caddr_t)False}, {XtNxorFillColor, XtCInt, XtRInt, sizeof(int), XtOffset(GtermWidget,gterm.gm_xorFillColor), XtRImmediate, (caddr_t)2}, {XtNxorFillBgColor, XtCInt, XtRInt, sizeof(int), XtOffset(GtermWidget,gterm.gm_xorFillBgColor), XtRImmediate, (caddr_t)255}, {XtNmarkerHighlightWidth, XtCInt, XtRInt, sizeof(int), XtOffset(GtermWidget,gterm.gm_highlightWidth), XtRImmediate, (caddr_t)2}, {XtNmarkerHighlightColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.gm_highlightColor), XtRString, "green"}, {XtNmarkerCursorFgColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.gm_cursorFgColor), XtRString, "yellow"}, {XtNmarkerCursorBgColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.gm_cursorBgColor), XtRString, "black"}, {XtNmarkerLineLineColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.gm_LineLineColor), XtRString, "green"}, {XtNmarkerLineKnotColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.gm_LineKnotColor), XtRString, "blue"}, {XtNmarkerLineKnotSize, XtCInt, XtRInt, sizeof(int), XtOffset(GtermWidget,gterm.gm_LineKnotSize), XtRImmediate, (caddr_t)5}, {XtNmarkerTextLineColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.gm_TextLineColor), XtRString, "green"}, {XtNmarkerTextColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.gm_TextColor), XtRString, "yellow"}, {XtNmarkerTextBgColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.gm_TextBgColor), XtRString, "SlateGray"}, {XtNmarkerTextBorder, XtCInt, XtRInt, sizeof(int), XtOffset(GtermWidget,gterm.gm_TextBorder), XtRImmediate, (caddr_t)2}, {XtNmarkerTextFont, XtCFont, XtRFontStruct, sizeof(XFontStruct *), XtOffset(GtermWidget,gterm.gm_TextFont), XtRString, "6x13"}, {XtNmarkerTextString, XtCString, XtRString, sizeof(String), XtOffset(GtermWidget,gterm.gm_TextString), XtRImmediate, (caddr_t)NULL}, {XtNmarkerRectLineColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.gm_RectLineColor), XtRString, "green"}, {XtNmarkerRectKnotColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.gm_RectKnotColor), XtRString, "blue"}, {XtNmarkerRectKnotSize, XtCInt, XtRInt, sizeof(int), XtOffset(GtermWidget,gterm.gm_RectKnotSize), XtRImmediate, (caddr_t)0}, {XtNmarkerBoxLineColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.gm_BoxLineColor), XtRString, "green"}, {XtNmarkerBoxKnotColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.gm_BoxKnotColor), XtRString, "blue"}, {XtNmarkerBoxKnotSize, XtCInt, XtRInt, sizeof(int), XtOffset(GtermWidget,gterm.gm_BoxKnotSize), XtRImmediate, (caddr_t)0}, {XtNmarkerCircleLineColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.gm_CircleLineColor), XtRString, "green"}, {XtNmarkerCircleKnotColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.gm_CircleKnotColor), XtRString, "blue"}, {XtNmarkerCircleKnotSize, XtCInt, XtRInt, sizeof(int), XtOffset(GtermWidget,gterm.gm_CircleKnotSize), XtRImmediate, (caddr_t)0}, {XtNmarkerEllipseLineColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.gm_EllipseLineColor), XtRString, "green"}, {XtNmarkerEllipseKnotColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.gm_EllipseKnotColor), XtRString, "blue"}, {XtNmarkerEllipseKnotSize, XtCInt, XtRInt, sizeof(int), XtOffset(GtermWidget,gterm.gm_EllipseKnotSize), XtRImmediate, (caddr_t)0}, {XtNmarkerPgonLineColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.gm_PgonLineColor), XtRString, "green"}, {XtNmarkerPgonKnotColor, XtCForeground, XtRPixel, sizeof(Pixel), XtOffset(GtermWidget,gterm.gm_PgonKnotColor), XtRString, "blue"}, {XtNmarkerPgonKnotSize, XtCInt, XtRInt, sizeof(int), XtOffset(GtermWidget,gterm.gm_PgonKnotSize), XtRImmediate, (caddr_t)5}, }; /* extern void HandlePopupMenu(); */ static Boolean SetValues(); static void Initialize(), Realize(), Destroy(), Redisplay(), Resize(); static void HandleIgnore(), HandleGraphicsInput(), HandleDisplayCrosshair(); static void HandleSoftReset(), HandleGraphicsContext(); static void HandleEnterWindow(), HandleLeaveWindow(); static void color_crosshair(), color_ginmodeCursor(); static void HandleTrackCursor(); static void savepos(), blink_cursor(); static void mp_linkafter(), mp_unlink(); Marker GmSelect(); static void M_create(), GtMarkerFree(); static void gm_focusin(), gm_focusout(), gm_refocus(); static void gm_request_translations(), gm_load_translations(); static int gm_curpos(); static set_default_color_index(); static inherit_default_colormap(); static update_default_colormap(); static update_transients(), update_cursor(); static request_colormap_focus(), restore_colormap_focus(); static refresh_source(), refresh_destination(), get_regions(); static get_rects(), scale_zoom(), scale_intzoom(), scale_boxcar(); static lw_convolve(), bx_boxcar(), bx_extract(), bx_interp(); static mf_getpixel(), mf_getinten(); static scale_lowpass(), scale_nearest(), scale_bilinear(); static save_mapping(), load_mapping(), get_pixel_mapping(); static update_mapping(), free_mapping(), valid_mapping(), rect_intersect(); static initialize_mapping(), draw_crosshair(), erase_crosshair(); static DrawContext get_draw_context(); static invalidate_draw_context(); static XPoint *mapVector(); static Colormap get_colormap(); static Cursor get_cursor(); static void init_iomap(), invalidate_cmap(); static Pixel get_pixel(), *get_cmap_in(), *get_cmap_out(); extern double atof(); static XtActionsRec gtermActionsList[] = { { "ignore", HandleIgnore }, { "graphics-input", HandleGraphicsInput }, { "crosshair", HandleDisplayCrosshair }, { "track-cursor", HandleTrackCursor }, { "enter-window", HandleEnterWindow }, { "leave-window", HandleLeaveWindow }, /* { "popup-menu", HandlePopupMenu }, */ { "reset", HandleSoftReset }, { "m_create", M_create }, }; GtermClassRec gtermClassRec = { { /* core fields */ /* superclass */ &widgetClassRec, /* class_name */ "Gterm", /* widget_size */ sizeof(GtermRec), /* class_initialize */ XawInitializeWidgetSet, /* class_part_initialize */ NULL, /* class_inited */ False, /* initialize */ Initialize, /* initialize_hook */ NULL, /* realize */ Realize, /* actions */ gtermActionsList, /* num_actions */ XtNumber(gtermActionsList), /* resources */ resources, /* resource_count */ XtNumber(resources), /* xrm_class */ (XrmClass)NULL, /* compress_motion */ True, /* compress_exposure */ True, /* compress_enterleave */ True, /* visible_interest */ False, /* destroy */ Destroy, /* resize */ Resize, /* expose */ Redisplay, /* set_values */ SetValues, /* set_values_hook */ NULL, /* set_values_almost */ XtInheritSetValuesAlmost, /* get_values_hook */ NULL, /* accept_focus */ NULL, /* version */ XtVersion, /* callback_private */ NULL, /* tm_table */ defaultGtermTranslations, /* query_geometry */ XtInheritQueryGeometry, /* display_accelerator */ XtInheritDisplayAccelerator, /* extension */ NULL } }; WidgetClass gtermWidgetClass = (WidgetClass) >ermClassRec; #define abs(a) (((a)<0)?(-(a)):(a)) #define max(a,b) ((a)>=(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) #define ERR (-1) #define OK 0 #define SQR(a) ((a)*(a)) /* * Widget class procedures. * -------------------------- */ /* ARGSUSED */ static void Initialize (request, new) Widget request, new; { register GtermWidget w = (GtermWidget)new; register GC gc; XColor fg_color, bg_color; XFontStruct **fp; Font cursor_font; Display *display; Screen *screen; Pixel *pp; int i; w->gterm.resetCallback = NULL; w->gterm.resizeCallback = NULL; w->gterm.inputCallback = NULL; w->gterm.display = display = XtDisplay (w); w->gterm.screen = screen = XtScreen (w); w->gterm.root = RootWindowOfScreen (screen); XtVaSetValues ((Widget)w, XtNbackground, (XtArgVal)w->gterm.color0, NULL); /* Initialize color map. */ pp = &w->gterm.color0; for (i=0; i < SZ_STATIC_CMAP; i++) w->gterm.color[i].pixel = w->gterm.cmap[i] = *pp++; for ( ; i < MAX_SZCMAP; i++) { memset ((char *)&w->gterm.color[i], 0, sizeof(XColor)); w->gterm.color[i].pixel = w->gterm.cmap[i] = i; } XQueryColors (display, w->core.colormap, w->gterm.color, SZ_STATIC_CMAP); w->gterm.ncolors = SZ_STATIC_CMAP; init_iomap (w); w->gterm.useDefaultCM = (strcmp (w->gterm.cmapName, "default") == 0); w->gterm.haveColormap = w->gterm.useDefaultCM; w->gterm.cmapLastUpdate = 0; w->gterm.cmapLastShadow = 0; w->gterm.in_window = 0; /* Get clear pixmap GC. */ gc = XCreateGC (display, w->gterm.root, 0, NULL); XSetBackground (display, gc, w->gterm.color0); XSetForeground (display, gc, w->gterm.color0); w->gterm.clearGC = gc; /* Get expose GC. */ gc = XCreateGC (display, w->gterm.root, 0, NULL); w->gterm.exposeGC = gc; /* Get graphics drawing GC. */ gc = XCreateGC (display, w->gterm.root, 0, NULL); XSetBackground (display, gc, w->gterm.color0); XSetForeground (display, gc, w->gterm.color1); XSetLineAttributes (display, gc, 1, LineSolid, CapButt, JoinMiter); w->gterm.drawGC = gc; /* Get dialog box GC. */ gc = XCreateGC (display, w->gterm.root, 0, NULL); XSetBackground (display, gc, w->gterm.dialogBgColor); XSetForeground (display, gc, w->gterm.dialogFgColor); /* XSetFunction (display, gc, GXcopyInverted); */ w->gterm.dialogGC = gc; /* Get crosshair cursor GC. */ gc = XCreateGC (display, w->gterm.root, 0, NULL); XSetBackground (display, gc, w->gterm.color0); XSetForeground (display, gc, w->gterm.crosshairCursorColor); XSetLineAttributes (display, gc, 0, LineSolid, CapButt, JoinMiter); w->gterm.cursorGC = gc; /* Get special cursors. */ bg_color.pixel = w->gterm.idleCursorBgColor; XQueryColor (display, w->core.colormap, &bg_color); fg_color.pixel = w->gterm.idleCursorFgColor; XQueryColor (display, w->core.colormap, &fg_color); w->gterm.idle_cursor = get_cursor (w, w->gterm.idleCursor); XRecolorCursor (display, w->gterm.idle_cursor, &fg_color, &bg_color); bg_color.pixel = w->gterm.busyCursorBgColor; XQueryColor (display, w->core.colormap, &bg_color); fg_color.pixel = w->gterm.busyCursorFgColor; XQueryColor (display, w->core.colormap, &fg_color); w->gterm.busy_cursor = get_cursor (w, w->gterm.busyCursor); XRecolorCursor (display, w->gterm.busy_cursor, &fg_color, &bg_color); bg_color.pixel = w->gterm.color0; XQueryColor (display, w->core.colormap, &bg_color); fg_color.pixel = w->gterm.crosshairCursorColor; XQueryColor (display, w->core.colormap, &fg_color); cursor_font = XLoadFont (display, "cursor"); w->gterm.crosshair_cursor = XCreateGlyphCursor (display, cursor_font, cursor_font, XC_crosshair, XC_crosshair, &fg_color, &bg_color); w->gterm.ginmodeCursor = XtNewString (w->gterm.ginmodeCursor); if (strcmp (w->gterm.ginmodeCursor, "full_crosshair") != 0) { bg_color.pixel = w->gterm.ginmodeCursorBgColor; XQueryColor (display, w->core.colormap, &bg_color); fg_color.pixel = w->gterm.ginmodeCursorFgColor; XQueryColor (display, w->core.colormap, &fg_color); w->gterm.ginmode_cursor = get_cursor (w, w->gterm.ginmodeCursor); XRecolorCursor (display, w->gterm.ginmode_cursor, &fg_color, &bg_color); w->gterm.ginmodeColors[0] = bg_color; w->gterm.ginmodeColors[1] = fg_color; } else w->gterm.ginmode_cursor = w->gterm.crosshair_cursor; w->gterm.full_crosshair = (strcmp (w->gterm.ginmodeCursor, "full_crosshair") == 0); /* Make sure we have all the fonts we need. */ for (fp = &w->gterm.alphaFont1, i=0; i < NAlphaFonts; i++, fp++) { if (*fp == NULL) { *fp = XQueryFont (display, XGContextFromGC (DefaultGCOfScreen(screen))); } w->gterm.alpha_fonts[i] = *fp; } for (fp = &w->gterm.dialogFont1, i=0; i < NDialogFonts; i++, fp++) { if (*fp == NULL) { *fp = XQueryFont (display, XGContextFromGC (DefaultGCOfScreen(screen))); } w->gterm.dialog_fonts[i] = *fp; } /* Raster initialization. */ w->gterm.rasters = NULL; w->gterm.nrasters = 0; w->gterm.mappings = NULL; w->gterm.nmappings = 0; w->gterm.mp_head = NULL; w->gterm.mp_tail = NULL; w->gterm.colormaps = NULL; w->gterm.wa_defined = 0; memset ((char *)&w->gterm.draw, 0, sizeof (struct drawContext)); /* Marker initialization. */ w->gterm.gm_head = NULL; w->gterm.gm_tail = NULL; w->gterm.gm_create = NULL; w->gterm.gm_active = NULL; w->gterm.defTranslations = NULL; w->gterm.nauxTrans = 0; w->gterm.gm_defTranslations = NULL; w->gterm.gm_curTranslations = NULL; w->gterm.gm_reqTranslations = NULL; w->gterm.gm_timer_id = (XtIntervalId) NULL; w->gterm.gm_initialized = False; /* Set defaults (some of these are clobbered anyway by Realize/Resize). */ w->gterm.raster = 0; w->gterm.cur_x = 0; w->gterm.cur_y = 0; w->gterm.last_x = 0; w->gterm.last_y = 0; w->gterm.cursor_drawn = 0; w->gterm.cursor_type = GtIdleCursor; w->gterm.pixmap = (Pixmap)NULL; w->gterm.d_pixmap = (Pixmap)NULL; w->gterm.preserve_screen = 0; w->gterm.preserve_valid = 0; w->gterm.d_saved = 0; w->gterm.alpha_font = DefaultAlphaFont; w->gterm.dialog_font = DefaultDialogFont; w->gterm.optcols = 80; w->gterm.optrows = 35; w->gterm.line_width = 1; w->gterm.data_level = GtSet; w->gterm.line_style = GtSolid; w->gterm.fill_type = GtSolid; set_default_color_index (w); /* Disable input until window is ready. */ w->gterm.delay = 1; } static void Realize (gw, valueMask, attrs) Widget gw; XtValueMask *valueMask; XSetWindowAttributes *attrs; { GtermWidget w = (GtermWidget) gw; XVisualInfo rvinfo, *vinfo = (XVisualInfo *) NULL; Visual *ourVisual = (Visual *) NULL; int i, nvis; /* Set default window size. */ XtMakeResizeRequest (gw, w->core.width, w->core.height, &w->core.width, &w->core.height); /* Should define pseudocolor visual here, if truecolor or directcolor * default visual. */ /* Create graphics window. We first look for an 8-Bit PseudoColor * visual to use, otherwise we fail. (MJF 8/22/97) XtCreateWindow (gw, InputOutput, (Visual *)CopyFromParent, *valueMask, attrs); */ vinfo = XGetVisualInfo (w->gterm.display, 0L, &rvinfo, &nvis); for (i=0; i < nvis; i++) if (vinfo[i].depth == 8 && vinfo[i].class == PseudoColor) ourVisual = vinfo[i].visual; if (ourVisual) XtCreateWindow (gw, InputOutput, ourVisual, *valueMask, attrs); else { fprintf (stderr, "No 8-bit PseudoColor visual found.\n"); exit(1); } w->gterm.window = XtWindow (gw); w->gterm.old_width = w->gterm.xres = w->core.width; w->gterm.old_height = w->gterm.yres = w->core.height; GtRasterInit (w); GtMarkerInit (w); if (w->core.visible) XDefineCursor (w->gterm.display, w->gterm.window, w->gterm.cursor = w->gterm.idle_cursor); Resize (gw); } static void Destroy (gw) Widget gw; { GtermWidget w = (GtermWidget) gw; register GtCallback *cb, *cb_next; Display *display = w->gterm.display; /* Get rid of any raster stuff. */ GtRasterInit (gw); XtFree ((char *)w->gterm.rasters); XtFree ((char *)w->gterm.mappings); /* Destroy any markers. */ GtMarkerFree (w); /* Can't use XtDestroyGC here; the source says it is broken and will * work only for applications that have only 1 display, and we have 2. * Also the documentation in Asente&Swick documents the calling sequence * incorrectly. */ XFreeGC (display, w->gterm.clearGC); XFreeGC (display, w->gterm.exposeGC); XFreeGC (display, w->gterm.drawGC); XFreeGC (display, w->gterm.dialogGC); XFreeGC (display, w->gterm.cursorGC); /* This one also proves problematic. When there are multiple gterm * widgets allocating the same cursor, succeeding calls for the same * cursor return the same cursor ID. When these widgets are later * destroyed, the first XFreeCursor succeeds but subsequent ones find * the referenced cursor undefined and the application boms with a * BadCursor error. This must be some problem with reference counts * in the X server. Cursors use minor amounts of resources and they * will probably be freed anyway when the display is closed, so we just * leave them defined here. * XFreeCursor (display, w->gterm.idle_cursor); XFreeCursor (display, w->gterm.busy_cursor); XFreeCursor (display, w->gterm.crosshair_cursor); if (w->gterm.ginmode_cursor != w->gterm.crosshair_cursor) XFreeCursor (display, w->gterm.ginmode_cursor); */ if (w->gterm.pixmap) XFreePixmap (w->gterm.display, w->gterm.pixmap); if (w->gterm.d_pixmap) XFreePixmap (w->gterm.display, w->gterm.d_pixmap); /* Destroy callback lists. */ for (cb = w->gterm.resetCallback; cb; cb = cb_next) { cb_next = cb->next; XtFree ((char *)cb); } for (cb = w->gterm.resizeCallback; cb; cb = cb_next) { cb_next = cb->next; XtFree ((char *)cb); } for (cb = w->gterm.inputCallback; cb; cb = cb_next) { cb_next = cb->next; XtFree ((char *)cb); } w->gterm.resetCallback = NULL; w->gterm.resizeCallback = NULL; w->gterm.inputCallback = NULL; XtFree (w->gterm.ginmodeCursor); } static void Resize (gw) Widget gw; { GtermWidget w = (GtermWidget) gw; register GtCallback *cb; int char_width, char_height, char_base; int bestfont, fonterr, dx, dy, i; unsigned int width, height, u_junk; GtCallback cbl[128]; XFontStruct *fp; int ncb, junk; Pixmap pixmap; Window root; if (!XtIsRealized(gw)) return; /* Create new pixmap. */ pixmap = XCreatePixmap (w->gterm.display, w->gterm.window, w->core.width + 1, w->core.height + 1, w->core.depth); if (pixmap) XFillRectangle (w->gterm.display, pixmap, w->gterm.clearGC, 0, 0, w->core.width, w->core.height); /* Copy old pixmap into new and free old pixmap. */ if (w->gterm.pixmap) { XGetGeometry (w->gterm.display, w->gterm.pixmap, &root, &junk, &junk, &width, &height, &u_junk, &u_junk); XSetClipMask (w->gterm.display, w->gterm.exposeGC, None); if (w->gterm.copyOnResize) XCopyArea (w->gterm.display, w->gterm.pixmap, pixmap, w->gterm.exposeGC, 0, 0, width-1, height-1, 0, 0); XFreePixmap (w->gterm.display, w->gterm.pixmap); } /* Install new pixmap. */ w->gterm.pixmap = pixmap; w->gterm.preserve_valid = 0; /* Redraw window. */ if (w->gterm.pixmap) { XCopyArea (w->gterm.display, w->gterm.pixmap, w->gterm.window, w->gterm.exposeGC, 0, 0, w->core.width, w->core.height, 0, 0); } /* Pick best alpha font. */ bestfont = 0; fonterr = 9999; for (i=0; i < NAlphaFonts; i++) { fp = w->gterm.alpha_fonts[i]; char_width = fp->max_bounds.width; char_height = fp->max_bounds.ascent + fp->max_bounds.descent; dx = (((int)w->core.width / char_width) - w->gterm.optcols) * 2; dy = ((int)w->core.height / char_height) - w->gterm.optrows; if (abs(dx) + abs(dy) < fonterr) { bestfont = i; fonterr = abs(dx) + abs(dy); } } w->gterm.alpha_font = bestfont; fp = w->gterm.alpha_fonts[w->gterm.alpha_font]; XSetFont (w->gterm.display, w->gterm.drawGC, fp->fid); /* Pick best dialog font. */ bestfont = 0; fonterr = 9999; for (i=0; i < NDialogFonts; i++) { fp = w->gterm.dialog_fonts[i]; char_width = fp->max_bounds.width; dx = ((int)w->core.width / char_width) - 80; if (abs(dx) < fonterr) { bestfont = i; fonterr = abs(dx); } } w->gterm.dialog_font = bestfont; fp = w->gterm.dialog_fonts[w->gterm.dialog_font]; char_height = fp->max_bounds.ascent + fp->max_bounds.descent; char_base = fp->max_bounds.ascent; w->gterm.d_xoff = 2; w->gterm.d_yoff = w->core.height - char_height - 2; w->gterm.d_height = char_height; XSetFont (w->gterm.display, w->gterm.dialogGC, fp->fid); /* Create dialog save area pixmap. */ if (w->gterm.d_pixmap) XFreePixmap (w->gterm.display, w->gterm.d_pixmap); w->gterm.d_pixmap = XCreatePixmap (w->gterm.display, w->gterm.window, w->core.width, char_height, w->core.depth); w->gterm.d_saved = 0; /* Adjust cursor position to allow for change in window size. */ w->gterm.cur_x = w->gterm.cur_x * (int)w->core.width / w->gterm.old_width; w->gterm.cur_y = w->gterm.cur_y * (int)w->core.height / w->gterm.old_height; w->gterm.old_width = w->core.width; w->gterm.old_height = w->core.height; if (w->gterm.cursor_type == GtGinmodeCursor) { XWarpPointer (w->gterm.display, w->gterm.window, w->gterm.window, 0,0,0,0, w->gterm.cur_x, w->gterm.cur_y); update_cursor (w); } /* Raster descriptor 0 must track the window size. */ if (w->gterm.rasters) { Raster rp = &w->gterm.rasters[0]; rp->width = w->core.width; rp->height = w->core.height; } /* Mark gterm widget ready for further client input. */ w->gterm.delay = 0; /* Call any resize callbacks. Callbacks can delete or add callbacks, * so make a copy of the callback list first. */ for (cb = w->gterm.resizeCallback, ncb=0; cb; cb = cb->next) cbl[ncb++] = *cb; for (i=0; i < ncb; i++) (*cbl[i].proc) (cbl[i].client_data, w); } /* ARGSUSED */ static void Redisplay (gw, event, region) Widget gw; XEvent *event; Region region; { register GtermWidget w = (GtermWidget) gw; register XExposeEvent *ev = (XExposeEvent *)event; int x, y, width, height; if (!XtIsRealized (gw)) return; if (event) { x = ev->x; y = ev->y; width = ev->width; height = ev->height; } else { x = 0; y = 0; width = w->core.width; height = w->core.height; } if (w->gterm.pixmap) { /* Clipping with the region argument does not work properly with * the OpenLook server for some reason - the clip region is one * pixel too small on the right and bottom. Until the reason for * this becomes clear, we use the bounding box provided in the * Expose event to roughly clip the refresh. * XSetClipOrigin (w->gterm.display, w->gterm.exposeGC, 0, 0); XSetRegion (w->gterm.display, w->gterm.exposeGC, region); */ XSetClipMask (w->gterm.display, w->gterm.exposeGC, None); XCopyArea (w->gterm.display, w->gterm.pixmap, w->gterm.window, w->gterm.exposeGC, x, y, width, height, x, y); } update_transients (w, region); /* A dummy expose event is used to ensure that the resize delay is * cleared, in the event that the resize request is not granted. */ if (ev && ev->send_event) w->gterm.delay = 0; } /* ARGSUSED */ static Boolean SetValues (current, request, set) Widget current, request, set; { GtermWidget old = (GtermWidget) current; GtermWidget req = (GtermWidget) request; register GtermWidget w = (GtermWidget) set; Display *display = w->gterm.display; Boolean redisplay = False; register GC gc; if (old->gterm.dialogBgColor != req->gterm.dialogBgColor) { gc = w->gterm.dialogGC; XSetBackground (display, gc, w->gterm.dialogBgColor); } if (old->gterm.dialogFgColor != req->gterm.dialogFgColor) { gc = w->gterm.dialogGC; XSetForeground (display, gc, w->gterm.dialogFgColor); } if (old->gterm.ginmodeCursor != req->gterm.ginmodeCursor) { static char *full_crosshair = "full_crosshair"; XtFree (old->gterm.ginmodeCursor); w->gterm.ginmodeCursor = XtNewString (w->gterm.ginmodeCursor); erase_crosshair (w); w->gterm.full_crosshair = (strcmp (w->gterm.ginmodeCursor, full_crosshair) == 0); if (w->gterm.full_crosshair) { w->gterm.ginmode_cursor = w->gterm.crosshair_cursor; color_crosshair (w); } else { w->gterm.ginmode_cursor = get_cursor (w, w->gterm.ginmodeCursor); color_ginmodeCursor (w); } if (w->gterm.cursor_type == GtGinmodeCursor && w->core.visible) XDefineCursor (display, w->gterm.window, w->gterm.cursor = w->gterm.ginmode_cursor); } if (old->gterm.crosshairCursorColor != req->gterm.crosshairCursorColor) { color_crosshair (w); } if (old->gterm.ginmodeCursorBgColor != req->gterm.ginmodeCursorBgColor || old->gterm.ginmodeCursorFgColor != req->gterm.ginmodeCursorFgColor) { color_ginmodeCursor (w); } return (XtIsRealized(current) ? redisplay : False); } static void color_crosshair (w) register GtermWidget w; { register Display *display = w->gterm.display; XColor fg_color, bg_color; Colormap defcmap; register GC gc; erase_crosshair (w); defcmap = DefaultColormapOfScreen (w->gterm.screen); bg_color.pixel = w->gterm.color0; XQueryColor (display, defcmap, &bg_color); fg_color.pixel = w->gterm.crosshairCursorColor; XQueryColor (display, defcmap, &fg_color); gc = w->gterm.cursorGC; XSetForeground (display, gc, w->gterm.crosshairCursorColor); XRecolorCursor (display, w->gterm.crosshair_cursor, &fg_color, &bg_color); w->gterm.ginmodeColors[0] = bg_color; w->gterm.ginmodeColors[1] = fg_color; update_cursor (w); } static void color_ginmodeCursor (w) register GtermWidget w; { register Display *display = w->gterm.display; XColor fg_color, bg_color; Colormap defcmap; defcmap = DefaultColormapOfScreen (w->gterm.screen); bg_color.pixel = w->gterm.ginmodeCursorBgColor; XQueryColor (display, defcmap, &bg_color); fg_color.pixel = w->gterm.ginmodeCursorFgColor; XQueryColor (display, defcmap, &fg_color); XRecolorCursor (display, w->gterm.ginmode_cursor, &fg_color, &bg_color); w->gterm.ginmodeColors[0] = bg_color; w->gterm.ginmodeColors[1] = fg_color; } /* * Action procedures. * ----------------------- */ /* ARGSUSED */ static void HandleIgnore (widget, event, params, param_count) Widget widget; XEvent *event; /* unused */ String *params; /* unused */ Cardinal *param_count; /* unused */ { /* ignore an event */ } /* ARGSUSED */ static void HandleGraphicsInput (widget, event, params, param_count) Widget widget; XEvent *event; /* unused */ String *params; /* unused */ Cardinal *param_count; /* unused */ { register GtermWidget w = (GtermWidget)widget; register XKeyEvent *ev = (XKeyEvent *) event; register GtCallback *cb; GtCallback cbl[128]; int ncb, i; /* Call any resize callbacks. Callbacks can delete or add callbacks, * so make a copy of the callback list first. */ for (cb = w->gterm.inputCallback, ncb=0; cb; cb = cb->next) cbl[ncb++] = *cb; for (i=0; i < ncb; i++) (*cbl[i].proc) (cbl[i].client_data, w, event); } /* ARGSUSED */ static void HandleDisplayCrosshair (widget, event, params, nparams) Widget widget; XEvent *event; /* unused */ String *params; /* unused */ Cardinal *nparams; /* unused */ { register GtermWidget w = (GtermWidget)widget; XButtonEvent *ev = &event->xbutton; /* Ignore if cursor is in a marker. */ if (w->gterm.gm_active) return; if (*nparams && strcmp (params[0], "on") == 0) { erase_crosshair (w); XDefineCursor (w->gterm.display, w->gterm.window, w->gterm.crosshair_cursor); draw_crosshair (w, ev->x, ev->y); } else if (*nparams && strcmp (params[0], "off") == 0) { erase_crosshair (w); XDefineCursor (w->gterm.display, w->gterm.window, w->gterm.cursor); } } /* ARGSUSED */ static void HandleTrackCursor (widget, event, params, param_count) Widget widget; XEvent *event; /* unused */ String *params; /* unused */ Cardinal *param_count; /* unused */ { register GtermWidget w = (GtermWidget)widget; XMotionEvent *ev = &event->xmotion; gmSelection what; Marker gm; savepos (w, (XEvent *)ev); if ((gm = GmSelect (w, ev->x, ev->y, &what))) gm_focusin (w, gm, &what); else if (w->gterm.gm_active) gm_focusout (w, 1); if (w->gterm.cursor_type == GtGinmodeCursor) if (w->gterm.full_crosshair) { erase_crosshair (w); draw_crosshair (w, ev->x, ev->y); /* Flushing here keeps cursor tracking synchronous and tends * to aid motion compression, by preventing crosshair draw * requests from being queued up for transmission to the * server. */ XFlush (w->gterm.display); } else { w->gterm.cur_x = ev->x; w->gterm.cur_y = ev->y; } } /* ARGSUSED */ static void HandleEnterWindow (widget, event, params, param_count) Widget widget; XEvent *event; /* unused */ String *params; /* unused */ Cardinal *param_count; /* unused */ { register GtermWidget w = (GtermWidget)widget; register XEnterWindowEvent *ev = (XEnterWindowEvent *) event; if (!w->gterm.useDefaultCM && w->gterm.haveColormap) { int update = w->gterm.cmapUpdate; /* To avoid excessive server queries the colormap is only updated * every so often. Updating is disabled if cmapUpdate is set to zero. if (update && ev->time - w->gterm.cmapLastUpdate > update * 1000) { */ if (update) { inherit_default_colormap (w); w->gterm.cmapLastUpdate = ev->time; } /* Advise the window manager to load our colormap. */ request_colormap_focus (w); } w->gterm.in_window++; } /* ARGSUSED */ static void HandleLeaveWindow (widget, event, params, param_count) Widget widget; XEvent *event; /* unused */ String *params; /* unused */ Cardinal *param_count; /* unused */ { register GtermWidget w = (GtermWidget)widget; register XLeaveWindowEvent *ev = (XLeaveWindowEvent *) event; if (!w->gterm.useDefaultCM && w->gterm.haveColormap) { int shadow = w->gterm.cmapShadow; /* The shadow option matches unused cells in the default colormap * with the colors in our custom colormap. if (shadow && ev->time - w->gterm.cmapLastShadow > shadow * 1000) { */ if (shadow) { update_default_colormap (w); w->gterm.cmapLastShadow = ev->time; } restore_colormap_focus (w); } w->gterm.in_window = 0; } /* ARGSUSED */ static void HandleSoftReset (widget, event, params, param_count) Widget widget; XEvent *event; /* unused */ String *params; /* unused */ Cardinal *param_count; /* unused */ { register GtermWidget w = (GtermWidget)widget; register GtCallback *cb; GtCallback cbl[128]; int ncb, i; GtReset (w); /* Call any resize callbacks. Callbacks can delete or add callbacks, * so make a copy of the callback list first. */ for (cb = w->gterm.resetCallback, ncb=0; cb; cb = cb->next) cbl[ncb++] = *cb; for (i=0; i < ncb; i++) (*cbl[i].proc) (cbl[i].client_data, w, NULL); } /* * GRAPHICS routines (public functions). * -------------------------------------- */ GtActivate (w) GtermWidget w; { w->gterm.interactive = 0; w->gterm.save_x = w->gterm.save_y = 0; } GtDeactivate (w) GtermWidget w; { Display *display = w->gterm.display; Window window = w->gterm.window; if (w->gterm.interactive) { if (w->gterm.save_x > 0 && w->gterm.save_y > 0) { if (w->gterm.warpCursor) { /* Workaround X server bug. */ if (w->gterm.root != w->gterm.save_root) XWarpPointer (display,None,w->gterm.root, 0,0,0,0, WidthOfScreen(w->gterm.screen) - 1, HeightOfScreen(w->gterm.screen) - 1); /* Move pointer to saved position. */ XWarpPointer (display, None, w->gterm.save_root, 0,0,0,0, w->gterm.save_x, w->gterm.save_y); } w->gterm.save_x = 0; w->gterm.save_y = 0; } w->gterm.interactive = 0; } } GtReady (w) GtermWidget w; { return (w->gterm.delay == 0); } GtReset (w) GtermWidget w; { invalidate_draw_context (w); set_default_color_index (w); XSetFunction (w->gterm.display, w->gterm.drawGC, GXcopy); XSetLineAttributes (w->gterm.display, w->gterm.drawGC, 1, LineSolid, CapButt, JoinMiter); /* Set defaults. */ w->gterm.line_width = 1; w->gterm.data_level = GtSet; w->gterm.line_style = GtSolid; w->gterm.fill_type = GtSolid; w->gterm.raster = 0; } GtTimerInhibit (w, state) GtermWidget w; Boolean state; { /* This is a kludge to allow a client (xgterm) to disable use of timers * if they don't work in a given implementation. */ w->gterm.useTimers = !state; } GtAugmentTranslations (w, translations) register GtermWidget w; char *translations; { register int i; if ((i = w->gterm.nauxTrans) < MAX_AUXTRANS) { w->gterm.auxTrans[i] = XtParseTranslationTable (translations); w->gterm.auxTType[i] = T_augment; w->gterm.nauxTrans++; XtAugmentTranslations ((Widget)w, w->gterm.auxTrans[i]); } } GtOverrideTranslations (w, translations) register GtermWidget w; char *translations; { register int i; if ((i = w->gterm.nauxTrans) < MAX_AUXTRANS) { w->gterm.auxTrans[i] = XtParseTranslationTable (translations); w->gterm.auxTType[i] = T_override; w->gterm.nauxTrans++; XtOverrideTranslations ((Widget)w, w->gterm.auxTrans[i]); } } GtFlush (w) GtermWidget w; { XFlush (w->gterm.display); } GtSetLogRes (w, width, height) GtermWidget w; int width, height; { w->gterm.xres = width; w->gterm.yres = height; } GtGetLogRes (w, width, height) GtermWidget w; int *width, *height; { *width = w->gterm.xres; *height = w->gterm.yres; } GtGetPhysRes (w, raster, width, height) GtermWidget w; int raster; /* zero for screen size */ int *width, *height; { if (raster) { register Raster rp = &w->gterm.rasters[raster]; *width = rp->width; *height = rp->height; } else { *width = w->core.width; *height = w->core.height; } } GtSetPhysRes (w, raster, width, height) GtermWidget w; int raster; int width, height; { GtCreateRaster (w, raster, GtServer, width, height, RasterDepth); } GtSetRaster (w, raster) GtermWidget w; int raster; { if (raster >= 0 && raster < w->gterm.maxRasters) { w->gterm.raster = raster; invalidate_draw_context (w); } } GtGetRaster (w) GtermWidget w; { return (w->gterm.raster); } /* ARGSUSED */ GtSetTextRes (w, optrows, optcols) GtermWidget w; int optrows, optcols; { w->gterm.optrows = optrows; w->gterm.optcols = optcols; } /* ARGSUSED */ GtSetCharSize (w, ival) GtermWidget w; int ival; { } GtSetDataLevel (w, ival) GtermWidget w; int ival; { invalidate_draw_context (w); switch (ival) { case GtSet: XSetFunction (w->gterm.display, w->gterm.drawGC, GXcopy); XSetForeground (w->gterm.display, w->gterm.drawGC, w->gterm.cmap[w->gterm.color_index]); w->gterm.data_level = ival; break; case GtClear: XSetFunction (w->gterm.display, w->gterm.drawGC, GXcopy); XSetForeground (w->gterm.display, w->gterm.drawGC, w->gterm.color0); w->gterm.data_level = ival; break; case GtInvert: /* This probably won't work correctly but leave it for now... */ XSetFunction (w->gterm.display, w->gterm.drawGC, GXxor); w->gterm.data_level = ival; break; } } GtSetLineWidth (w, ival) GtermWidget w; int ival; { w->gterm.line_width = ival; GtSetLineStyle (w, w->gterm.line_style); } #define Dashed "\010\003" #define Dotted "\002\003" #define DashDot "\016\003\001\003" #define Dash3Dot "\024\003\001\003\001\003\001\003" GtSetLineStyle (w, ival) GtermWidget w; int ival; { int line_width = w->gterm.line_width; int line_style = LineSolid; int cap_style = CapButt; int join_style = JoinMiter; int dash_offset = 0; char *dash_list = NULL; int dash_list_length = 0; switch (ival) { case GtSolid: w->gterm.line_style = ival; break; case GtDashed: line_style = LineOnOffDash; dash_list = (char *)Dashed; dash_list_length = strlen(Dashed); w->gterm.line_style = ival; break; case GtDotted: line_style = LineOnOffDash; dash_list = (char *)Dotted; dash_list_length = strlen(Dotted); w->gterm.line_style = ival; break; case GtDashDot: line_style = LineOnOffDash; dash_list = (char *)DashDot; dash_list_length = strlen(DashDot); w->gterm.line_style = ival; break; case GtDash3Dot: line_style = LineOnOffDash; dash_list = (char *)Dash3Dot; dash_list_length = strlen(Dash3Dot); w->gterm.line_style = ival; break; } if (dash_list_length) XSetDashes (w->gterm.display, w->gterm.drawGC, dash_offset, dash_list, dash_list_length); XSetLineAttributes (w->gterm.display, w->gterm.drawGC, line_width, line_style, cap_style, join_style); invalidate_draw_context (w); } GtSetColorIndex (w, ival) GtermWidget w; int ival; { register int color = w->gterm.iomap[ival]; if (color >= 0 && color < w->gterm.ncolors) { XSetForeground (w->gterm.display, w->gterm.drawGC, w->gterm.cmap[color]); w->gterm.color_index = color; invalidate_draw_context (w); } } GtSetFillType (w, ival) GtermWidget w; int ival; { switch (ival) { case GtSolid: case GtOutline: w->gterm.fill_type = ival; break; } } GtClearScreen (w) GtermWidget w; { register Mapping mp; if (!XtIsRealized ((Widget)w)) return; if (w->gterm.pixmap) XFillRectangle (w->gterm.display, w->gterm.pixmap, w->gterm.clearGC, 0, 0, w->core.width, w->core.height); XClearWindow (w->gterm.display, w->gterm.window); set_default_color_index (w); XSetFunction (w->gterm.display, w->gterm.drawGC, GXcopy); XSetForeground (w->gterm.display, w->gterm.drawGC, w->gterm.color1); XSetLineAttributes (w->gterm.display, w->gterm.drawGC, 1, LineSolid, CapRound, JoinRound); w->gterm.line_width = 1; w->gterm.line_style = GtSolid; w->gterm.fill_type = GtSolid; w->gterm.data_level = GtSet; w->gterm.preserve_valid = 0; w->gterm.gm_redisplay = 1; w->gterm.d_saved = 0; /* Mark any screen mappings to be unconditionally refreshed. */ for (mp = w->gterm.mp_head; mp; mp = mp->next) if (mp->enabled && mp->dst == 0) mp->refresh++; invalidate_draw_context (w); update_transients (w, NULL); } GtDrawPolyline (w, pv, npts) GtermWidget w; XPoint *pv; int npts; { XPoint *points, o_pv[MAX_POINTS]; DrawContext dx = get_draw_context (w); register MappingContext mx; register int i; for (i=0; i < dx->nmappings; i++) { mx = &dx->mapContext[i]; points = mx->scale ? mapVector(mx,pv,o_pv,npts) : pv; /* Add code to support max display request size. */ if (mx->use_backing_store) XDrawLines (w->gterm.display, w->gterm.pixmap, mx->drawGC, points, npts, CoordModeOrigin); XDrawLines (w->gterm.display, mx->pixmap, mx->drawGC, points, npts, CoordModeOrigin); } update_transients (w, (Region)NULL); } GtDrawPolymarker (w, pv, npts) GtermWidget w; XPoint *pv; int npts; { XPoint *points, o_pv[MAX_POINTS]; DrawContext dx = get_draw_context (w); register MappingContext mx; register int i; for (i=0; i < dx->nmappings; i++) { mx = &dx->mapContext[i]; points = mx->scale ? mapVector(mx,pv,o_pv,npts) : pv; /* Add code to support max display request size. */ if (mx->use_backing_store) XDrawPoints (w->gterm.display, w->gterm.pixmap, mx->drawGC, points, npts, CoordModeOrigin); XDrawPoints (w->gterm.display, mx->pixmap, mx->drawGC, points, npts, CoordModeOrigin); } update_transients (w, (Region)NULL); } GtDrawPolygon (w, pv, npts) GtermWidget w; XPoint *pv; int npts; { XPoint *points, o_pv[MAX_POINTS]; DrawContext dx = get_draw_context (w); register MappingContext mx; register int i; for (i=0; i < dx->nmappings; i++) { mx = &dx->mapContext[i]; points = mx->scale ? mapVector(mx,pv,o_pv,npts) : pv; if (w->gterm.fill_type == GtOutline) { /* Draw outline of region. */ int first = 0; int last = npts - 1; if (mx->use_backing_store) XDrawLines (w->gterm.display, w->gterm.pixmap, mx->drawGC, points, npts, CoordModeOrigin); XDrawLines (w->gterm.display, mx->pixmap, mx->drawGC, points, npts, CoordModeOrigin); if (points[last].x != points[first].x || points[last].y != points[first].y) { if (mx->use_backing_store) XDrawLine (w->gterm.display, w->gterm.pixmap, mx->drawGC, points[last].x, points[last].y, points[first].x, points[first].y); XDrawLine (w->gterm.display, mx->pixmap, mx->drawGC, points[last].x, points[last].y, points[first].x, points[first].y); } } else { /* Fill the outlined area. */ if (mx->use_backing_store) { XFillPolygon (w->gterm.display, w->gterm.pixmap, mx->drawGC, points, npts, Nonconvex, CoordModeOrigin); XDrawLines (w->gterm.display, w->gterm.pixmap, mx->drawGC, points, npts, CoordModeOrigin); } XFillPolygon (w->gterm.display, mx->pixmap, mx->drawGC, points, npts, Nonconvex, CoordModeOrigin); XDrawLines (w->gterm.display, mx->pixmap, mx->drawGC, points, npts, CoordModeOrigin); } } update_transients (w, (Region)NULL); } GtDrawMarker (w, x, y, xsize, ysize, type) GtermWidget w; int x, y; int xsize, ysize; int type; { } GtBell (w) GtermWidget w; { XBell (w->gterm.display, 0); } /* GtSetCursorPos -- Set the cursor position to the given coordinates X,Y. * Coordinates are specified in the current graphics coordinate system, * defined by the current raster and logical resolution. * * This routine is a little more complex than one might think due to the * complication of mappings. Screen coordinates are required to set the * cursor, but the graphics drawing context may be defined relative to * any raster. In the general case a graphics pipeline defines the series * of coordinate transformations required to transform from graphics * coordinates to screen coordinates. Things are further complicated since * the pipeline or desired position may not map to the screen, or there * may be multiple mappings to the screen. The first case (no mapping to * the screen) is dealt with by ignoring the request to warp the cursor. * The second case (one-to-many mapping) is dealt with by a heuristic: * the most recent screen coordinates are unmapped back to the raster we * are "drawing" into, defining a unique path through the mappings which * we can use to map back to the screen. * * The simplest case occurs when we are drawing directly into the screen. * In this case (raster=0) there may still be a logical to physical * coordinate transformation, but there are no mappings to complicate things. */ GtSetCursorPos (w, x, y) GtermWidget w; int x, y; { register MappingContext mx; register DrawContext dx; register Mapping mp; Window window = w->gterm.window; int sv_raster = w->gterm.raster; int sv_xres = w->gterm.xres, sv_yres = w->gterm.yres; int rasters[256], mappings[256], nmap=0, ntrans=0; int rx, ry, src, dst, map, i, npts = 1; int raster = w->gterm.raster; XPoint pv1[1], pv2[2]; XPoint *points, pv[1]; Raster rp; if (!XtIsRealized ((Widget)w)) return; /* Follow the current cursor position back to the source raster if * possible. This gives us a default pipeline to follow in the reverse * direction to map raster coordinates to screen coordinates, and is * necessary to find the right mappings when multiple mappings are * defined on a single source. */ rx = w->gterm.last_x; ry = w->gterm.last_y; src = 0; do { src = GtSelectRaster (w, dst=src, GtPixel,rx,ry, GtPixel,&rx,&ry,&map); if (src != dst) { rasters[nmap] = src; mappings[nmap++] = map; } } while (src != dst && src != raster); /* Ray trace the point through all of the mappings to the screen. * This isn't fully general, but gives us the capability to follow * most graphics pipelines to a point on the screen. */ do { GtSetRaster (w, raster); if (ntrans++) { /* After the first transformation we have raster coordinates, * so set the logical resolution to the raster dimensions. */ rp = &w->gterm.rasters[raster]; GtSetLogRes (w, rp->width, rp->height); } dx = get_draw_context (w); if (!dx->nmappings) return; /* Try to find the next mapping. */ if (nmap && rasters[nmap-1] == raster) for (i=0; i < dx->nmappings; i++) { mx = &dx->mapContext[i]; if (mx->mapping == mappings[nmap-1]) { mp = mx->mp; nmap--; goto havemap; } } for (i=0; i < dx->nmappings; i++) { mx = &dx->mapContext[i]; mp = mx->mp; if (mp && mp->dst == 0) break; } if (i >= dx->nmappings) { mx = &dx->mapContext[0]; mp = mx->mp; } havemap: rp = &w->gterm.rasters[mp ? mp->dst : raster]; /* Compute the coordinates points[0].{x,y} of the point x,y in the * destination raster. */ if (mx->scale) { /* Scaling is in effect. The following subterfuge is used to * compute the coordinates of the center of the raster pixel (x,y) * when the image is zoomed. We want to set the cursor to the * center of the selected pixel, not the edge. */ pv[0].x = x; pv[0].y = y; mapVector (mx, pv, pv1, npts); pv[0].x = x + 1; pv[0].y = y + 1; mapVector (mx, pv, pv2, npts); pv[0].x = (pv1[0].x + pv2[0].x) / 2.0; pv[0].y = (pv1[0].y + pv2[0].y) / 2.0; points = pv; } else { /* No scaling. */ pv[0].x = x; pv[0].y = y; points = pv; } /* Clip to the bounds of the destination raster and generate the * new x,y. */ x = max(0, min(rp->width-1, points[0].x)); y = max(0, min(rp->height-1, points[0].y)); } while (mp && (raster = mp->dst)); XWarpPointer (w->gterm.display, window, window, 0,0,0,0, x,y); w->gterm.last_x = w->gterm.cur_x = x; w->gterm.last_y = w->gterm.cur_y = y; GtSetRaster (w, sv_raster); GtSetLogRes (w, sv_xres, sv_yres); } GtGetCursorPos (w, x, y) GtermWidget w; int *x, *y; { *x = w->gterm.last_x; *y = w->gterm.last_y; } GtSetCursorType (w, type) GtermWidget w; int type; { static XtIntervalId id = (XtIntervalId) NULL; Display *display = w->gterm.display; Cursor cursor; int interval; Widget pw; if (!XtIsRealized ((Widget)w)) return; if (w->gterm.cursor_type == type) return; switch (w->gterm.cursor_type = type) { case GtNoCursor: case GtIdleCursor: erase_crosshair (w); cursor = w->gterm.idle_cursor; break; case GtGinmodeCursor: /* Begin graphics cursor mode. */ /* If a screen clear or drawing operation has caused the redisplay * flag to be set, redisplay any marker overlays. */ if (w->gterm.gm_redisplay) { GmRedisplay (w, (Region)NULL); w->gterm.gm_redisplay = False; } /* Make sure the window is visible. */ if (w->gterm.raiseWindow || w->gterm.deiconifyWindow) for (pw = (Widget)w; pw; pw = XtParent(pw)) if (XtIsShell(pw)) { if (w->gterm.deiconifyWindow) XMapWindow (display, XtWindow(pw)); if (w->gterm.raiseWindow) XRaiseWindow (display, XtWindow(pw)); } /* The first time this is done after a GtActivate causes the cursor * to be warped into the graphics window. The interactive flag is set * to cause GtDeactivate to restore the cursor to its original position * after the graphics interaction finishes. */ if (w->gterm.warpCursor) { int root_x, root_y, win_x, win_y; int xoff, yoff, width, height, x, y; Window gtermwin, root, child; unsigned int keys; int in_window = 0; width = w->core.width; height = w->core.height; gtermwin = w->gterm.window; XTranslateCoordinates (display, gtermwin, w->gterm.root, w->core.x, w->core.y, &xoff, &yoff, &child); if (XQueryPointer (display, w->gterm.root, &root, &child, &root_x, &root_y, &win_x, &win_y, &keys)) { /* Already in gterm window? */ if ((root_x >= xoff && root_x < xoff+width) && (root_y >= yoff && root_y < yoff+height)) { if (!w->gterm.interactive) { w->gterm.save_x = 0; w->gterm.save_y = 0; w->gterm.interactive++; } x = root_x - xoff; y = root_y - yoff; in_window++; } else { if (!w->gterm.interactive) { w->gterm.save_x = root_x; w->gterm.save_y = root_y; w->gterm.save_root = root; w->gterm.interactive++; } x = w->gterm.cur_x; y = w->gterm.cur_y; } } else { /* Pointer not on the current screen. */ if (!w->gterm.interactive) { w->gterm.save_x = root_x; w->gterm.save_y = root_y; w->gterm.save_root = root; w->gterm.interactive++; } x = w->gterm.cur_x; y = w->gterm.cur_y; } if ((x < 5 || x > width-5) || (y < 5 || y > height-5)) { x = width / 2; y = height / 2; } if (!in_window) { erase_crosshair (w); if (w->gterm.warpCursor) { XWindowAttributes wa; if (XGetWindowAttributes (display, gtermwin, &wa) && wa.map_state == IsViewable) { /* The following should not be necessary but is needed * to workaround an X server bug. When warping to a * different screen the pointer is not erased on the * old screen. It is hard to erase it, but we can * at least move it to the corner of the screen. */ if (root != w->gterm.root) { Screen *screen = w->gterm.screen; if (XGetWindowAttributes (display, root, &wa)) screen = wa.screen; XWarpPointer (display,None,root, 0,0,0,0, WidthOfScreen(screen) - 1, HeightOfScreen(screen) - 1); } /* Now warp into the gterm window. */ XWarpPointer (display, None, gtermwin, 0,0,0,0, x,y); } if (w->gterm.full_crosshair) draw_crosshair (w, x, y); } } else if (w->gterm.full_crosshair) { erase_crosshair (w); draw_crosshair (w, x, y); } } else update_cursor (w); cursor = w->gterm.ginmode_cursor; if (interval = w->gterm.ginmodeBlinkInterval) { XtAppContext appcon = XtWidgetToApplicationContext ((Widget) w); id = XtAppAddTimeOut (appcon, interval, blink_cursor, (XtPointer)w); } else id = (XtIntervalId) NULL; break; case GtBusyCursor: /* Exit graphics cursor mode. */ erase_crosshair (w); cursor = w->gterm.busy_cursor; break; } if (w->core.visible) XDefineCursor (w->gterm.display, w->gterm.window, w->gterm.cursor = cursor); if (id && w->gterm.cursor_type != GtGinmodeCursor) { XtRemoveTimeOut (id); id = (XtIntervalId) NULL; } } static void blink_cursor (w, id) GtermWidget w; XtIntervalId *id; { XtAppContext app_context; XColor bg, fg; int interval; bg = w->gterm.ginmodeColors[1]; fg = w->gterm.ginmodeColors[0]; app_context = XtWidgetToApplicationContext ((Widget) w); XRecolorCursor (w->gterm.display, w->gterm.ginmode_cursor, &fg, &bg); XFlush (w->gterm.display); w->gterm.ginmodeColors[0] = bg; w->gterm.ginmodeColors[1] = fg; if (interval = w->gterm.ginmodeBlinkInterval) XtAppAddTimeOut (app_context, interval, (XtTimerCallbackProc) blink_cursor, (XtPointer)w); } GtPostInputProc (w, userfcn, client_data) GtermWidget w; GtCallbackProc userfcn; XtPointer client_data; { register GtCallback *cb, *new; new = (GtCallback *) XtMalloc (sizeof (GtCallback)); new->proc = userfcn; new->client_data = client_data; new->next = NULL; for (cb = w->gterm.inputCallback; cb && cb->next; cb = cb->next) ; if (cb) cb->next = new; else w->gterm.inputCallback = new; } GtDeleteInputProc (w, userfcn, client_data) GtermWidget w; GtCallbackProc userfcn; XtPointer client_data; { register GtCallback *cb, *prev; for (prev=NULL, cb = w->gterm.inputCallback; cb; cb = cb->next) if (cb->proc == userfcn && cb->client_data == client_data) { if (prev) prev->next = cb->next; else w->gterm.inputCallback = cb->next; XtFree ((char *)cb); break; } else prev = cb; } GtPostResetProc (w, userfcn, client_data) GtermWidget w; GtCallbackProc userfcn; XtPointer client_data; { register GtCallback *cb, *new; new = (GtCallback *) XtMalloc (sizeof (GtCallback)); new->proc = userfcn; new->client_data = client_data; new->next = NULL; for (cb = w->gterm.resetCallback; cb && cb->next; cb = cb->next) ; if (cb) cb->next = new; else w->gterm.resetCallback = new; } GtDeleteResetProc (w, userfcn, client_data) GtermWidget w; GtCallbackProc userfcn; XtPointer client_data; { register GtCallback *cb, *prev; for (prev=NULL, cb = w->gterm.resetCallback; cb; cb = cb->next) if (cb->proc == userfcn && cb->client_data == client_data) { if (prev) prev->next = cb->next; else w->gterm.resetCallback = cb->next; XtFree ((char *)cb); break; } else prev = cb; } GtPostResizeProc (w, userfcn, client_data) GtermWidget w; GtCallbackProc userfcn; XtPointer client_data; { register GtCallback *cb, *new; new = (GtCallback *) XtMalloc (sizeof (GtCallback)); new->proc = userfcn; new->client_data = client_data; new->next = NULL; for (cb = w->gterm.resizeCallback; cb && cb->next; cb = cb->next) ; if (cb) cb->next = new; else w->gterm.resizeCallback = new; } GtDeleteResizeProc (w, userfcn, client_data) GtermWidget w; GtCallbackProc userfcn; XtPointer client_data; { register GtCallback *cb, *prev; for (prev=NULL, cb = w->gterm.resizeCallback; cb; cb = cb->next) if (cb->proc == userfcn && cb->client_data == client_data) { if (prev) prev->next = cb->next; else w->gterm.resizeCallback = cb->next; XtFree ((char *)cb); break; } else prev = cb; } GtDrawAlphaText (w, x, y, text) GtermWidget w; int x, y; char *text; { XPoint *points, pv[1], o_pv[1]; DrawContext dx = get_draw_context (w); register MappingContext mx; register int npts, i; pv[0].x = x; pv[0].y = y; npts = 1; for (i=0; i < dx->nmappings; i++) { mx = &dx->mapContext[i]; points = mx->scale ? mapVector(mx,pv,o_pv,npts) : pv; x = points[0].x; y = points[0].y; if (mx->use_backing_store) XDrawString (w->gterm.display, w->gterm.pixmap, mx->drawGC, x, y, text, strlen(text)); XDrawString (w->gterm.display, mx->pixmap, mx->drawGC, x, y, text, strlen(text)); } update_transients (w, (Region)NULL); } GtGetAlphaTextSize (w, string, width, height, base) GtermWidget w; char *string; int *width, *height, *base; { XFontStruct *fp; fp = w->gterm.alpha_fonts[w->gterm.alpha_font]; if (string) *width = XTextWidth (fp, string, strlen(string)); else *width = fp->max_bounds.width; *height = fp->max_bounds.ascent + fp->max_bounds.descent; *base = fp->max_bounds.ascent; } GtWriteAlphaCursor (w, x, y) GtermWidget w; int x, y; { } GtEraseAlphaCursor (w) GtermWidget w; { } GtStartDialog (w) GtermWidget w; { if (w->gterm.d_pixmap) if (w->gterm.d_saved) { GtEraseDialog (w); } else { XSetClipMask (w->gterm.display, w->gterm.exposeGC, None); XCopyArea (w->gterm.display, w->gterm.pixmap ? w->gterm.pixmap : w->gterm.window, w->gterm.d_pixmap, w->gterm.exposeGC, 0, w->gterm.d_yoff, w->core.width, w->gterm.d_height, 0, 0); w->gterm.d_saved = 1; } } GtEndDialog (w) GtermWidget w; { GtEraseDialog (w); w->gterm.d_saved = 0; } GtEraseDialog (w) GtermWidget w; { if (w->gterm.d_pixmap && w->gterm.d_saved) { XSetClipMask (w->gterm.display, w->gterm.exposeGC, None); XCopyArea (w->gterm.display, w->gterm.d_pixmap, w->gterm.window, w->gterm.exposeGC, 0, 0, w->core.width, w->gterm.d_height, 0, w->gterm.d_yoff); if (w->gterm.pixmap) XCopyArea (w->gterm.display, w->gterm.d_pixmap, w->gterm.pixmap, w->gterm.exposeGC, 0, 0, w->core.width, w->gterm.d_height, 0, w->gterm.d_yoff); update_transients (w, (Region)NULL); } } GtDrawDialogText (w, x, y, text) GtermWidget w; int x, y; char *text; { int xpos = w->gterm.d_xoff + x; int ypos = w->gterm.d_yoff + y; if (w->gterm.pixmap) XDrawImageString (w->gterm.display, w->gterm.pixmap, w->gterm.dialogGC, xpos, ypos, text, strlen(text)); XDrawImageString (w->gterm.display, w->gterm.window, w->gterm.dialogGC, xpos, ypos, text, strlen(text)); } GtGetDialogTextSize (w, string, width, height, base) GtermWidget w; char *string; int *width, *height, *base; { XFontStruct *fp; fp = w->gterm.dialog_fonts[w->gterm.dialog_font]; if (string) *width = XTextWidth (fp, string, strlen(string)); else *width = fp->max_bounds.width; *height = fp->max_bounds.ascent + fp->max_bounds.descent; *base = fp->max_bounds.ascent; } /* * Internal functions for above code. * ---------------------------------------- */ static set_default_color_index (w) GtermWidget w; { /* The default color index is 1, corresponding to the foreground * drawing color color1. Index zero is the background drawing color * color0. The remaining NColors color table entries are the optional * drawing colors corresponding to resources "color2" through "colorN". * These are used only if explicitly selected by the client application. */ XSetForeground (w->gterm.display, w->gterm.drawGC, w->gterm.cmap[1]); w->gterm.color_index = 1; invalidate_draw_context (w); } static draw_crosshair (w, x, y) GtermWidget w; int x, y; { if (!XtIsRealized ((Widget)w)) return; if (w->gterm.pixmap) { /* The preserve_screen flag is set if we need to preserve the * exact display window contents, rather than merely refresh from * the backing store pixmap. */ if (w->gterm.preserve_screen) { if (!w->gterm.preserve_valid || y != w->gterm.cur_y) XCopyArea (w->gterm.display, w->gterm.window, w->gterm.pixmap, w->gterm.exposeGC, 0, y, w->core.width, 1, 0, w->core.height); if (!w->gterm.preserve_valid || x != w->gterm.cur_x) XCopyArea (w->gterm.display, w->gterm.window, w->gterm.pixmap, w->gterm.exposeGC, x, 0, 1, w->core.height, w->core.width, 0); w->gterm.preserve_valid = 1; } XDrawLine (w->gterm.display, w->gterm.window, w->gterm.cursorGC, 0, y, w->core.width, y); XDrawLine (w->gterm.display, w->gterm.window, w->gterm.cursorGC, x, 0, x, w->core.height); XFlush (w->gterm.display); w->gterm.cursor_drawn++; } w->gterm.cur_x = x; w->gterm.cur_y = y; } static erase_crosshair (w) GtermWidget w; { if (!XtIsRealized ((Widget)w)) return; if (w->gterm.cursor_drawn) { register int x = w->gterm.cur_x; register int y = w->gterm.cur_y; XSetClipMask (w->gterm.display, w->gterm.exposeGC, None); if (w->gterm.pixmap) { if (w->gterm.preserve_screen && w->gterm.preserve_valid) { XCopyArea (w->gterm.display, w->gterm.pixmap, w->gterm.window, w->gterm.exposeGC, 0, w->core.height, w->core.width, 1, 0, y); XCopyArea (w->gterm.display, w->gterm.pixmap, w->gterm.window, w->gterm.exposeGC, w->core.width, 0, 1, w->core.height, x, 0); } else { XCopyArea (w->gterm.display, w->gterm.pixmap, w->gterm.window, w->gterm.exposeGC, 0, y, w->core.width, 1, 0, y); XCopyArea (w->gterm.display, w->gterm.pixmap, w->gterm.window, w->gterm.exposeGC, x, 0, 1, w->core.height, x, 0); } } w->gterm.cursor_drawn = 0; w->gterm.preserve_valid = 0; } } static update_transients (w, region) GtermWidget w; Region region; { /* If an explicit region is given redisplay any markers in it immediately, * otherwise set the redisplay flag to cause a full screen redisplay when * drawing finishes and the widget is ready for input. */ if ((char *)region) GmRedisplay (w, region); else w->gterm.gm_redisplay = True; /* Update the crosshair cursor if GIN mode is in effect. */ update_cursor (w); } static update_cursor (w) GtermWidget w; { if (w->gterm.cursor_type == GtGinmodeCursor && w->gterm.full_crosshair) { register int x = w->gterm.cur_x; register int y = w->gterm.cur_y; if (x || y) draw_crosshair (w, x, y); } } static Cursor get_cursor (w, cursor_name) GtermWidget w; String cursor_name; { XrmValue from, to; Cursor cursor; from.size = strlen (cursor_name) + 1; from.addr = cursor_name; to.addr = (caddr_t) &cursor; to.size = sizeof(cursor); if (!XtConvertAndStore ((Widget)w, XtRString, &from, XtRCursor, &to)) cursor = XCreateFontCursor (w->gterm.display, XC_crosshair); return (cursor); } static DrawContext get_draw_context (w) GtermWidget w; { DrawContext dx = &w->gterm.draw; if (!dx->valid) { int raster = w->gterm.raster; Raster rp = &w->gterm.rasters[raster]; register MappingContext mx = &dx->mapContext[0]; Region clip_region, mask_region; struct mapping *map, *mp, *np, p_mp; int xres = w->gterm.xres; int yres = w->gterm.yres; float xscale, yscale; XRectangle r; int i, j; dx->raster = w->gterm.raster; dx->rp = rp; if (raster == 0) { dx->nmappings = 1; mx->mapping = 0; mx->mp = NULL; mx->use_backing_store = (w->gterm.pixmap != (Pixmap)NULL); mx->pixmap = w->gterm.window; mx->drawGC = w->gterm.drawGC; mx->GC_private = 0; mx->xoffset = mx->yoffset = 0; /* (7/16/97) MJF - we don't scale raster 0 since it's already in screen coords. if (xres == rp->width && yres == rp->height) mx->scale = 0; else { mx->xscale = (float)rp->width / (float)xres; mx->yscale = (float)rp->height / (float)yres; mx->scale = 1; } */ mx->scale = 0; } else { dx->nmappings = 0; for (mp = w->gterm.mp_head; mp; mp = mp->next) { if (!mp->enabled || mp->src != raster || w->gterm.rasters[mp->dst].type != GtServer) continue; if (!valid_mapping (w, mp)) continue; mx->mp = mp; mx->mapping = mp->mapping; mx->pixmap = w->gterm.rasters[mp->dst].r.pixmap; mx->use_backing_store = (mp->dst == 0 && w->gterm.pixmap && !(mp->rop & R_Transient)); /* Determine if any scaling is necessary. */ get_pixel_mapping (w, mp, &p_mp, 0); map = &p_mp; /* Compute logical-to-raster scaling. */ mx->xoffset = mx->yoffset = 0; if (xres == rp->width && yres == rp->height) { mx->xscale = mx->yscale = 1.0; mx->scale = 0; } else { mx->xscale = (float)rp->width / (float)xres; mx->yscale = (float)rp->height / (float)yres; mx->scale = 1; } /* Compute overall scale factors by combining logical-to- * raster and raster-to-screen mappings. */ if (map->snx != map->dnx || map->sny != map->dny || map->sx != map->dx || map->sy != map->dy) { xscale = (float)map->dnx / (float)map->snx; mx->xscale *= xscale; if (xscale < 0) mx->xoffset = map->dx + abs(map->dnx) - 1; else mx->xoffset = map->dx; mx->xoffset -= (map->sx * xscale); yscale = (float)map->dny / (float)map->sny; mx->yscale *= yscale; if (yscale < 0) mx->yoffset = map->dy + abs(map->dny) - 1; else mx->yoffset = map->dy; mx->yoffset -= (map->sy * yscale); mx->scale = 1; } /* Compute the clip mask which will clip graphics to the * destination rect of the mapping, minus any regions of * this rect covered by other mappings. */ clip_region = XCreateRegion(); r.x = map->dx; r.y = map->dy; r.width = abs(map->dnx); r.height = abs(map->dny); XUnionRectWithRegion (&r, clip_region, clip_region); for (np = mp->next; np; np = np->next) { struct mapping p_np; if (!np->enabled || np->dst != mp->dst) continue; get_pixel_mapping (w, np, &p_np, 0); mask_region = XCreateRegion(); r.x = p_np.dx; r.y = p_np.dy; r.width = abs(p_np.dnx); r.height = abs(p_np.dny); XUnionRectWithRegion (&r, mask_region, mask_region); XSubtractRegion (clip_region, mask_region, clip_region); XDestroyRegion (mask_region); } /* Create a drawing GC which is a copy of the global drawGC * but using the clip mask computed above. */ mx->drawGC = XCreateGC (w->gterm.display, w->gterm.root, 0, NULL); XCopyGC (w->gterm.display, w->gterm.drawGC, ~0, mx->drawGC); XSetRegion (w->gterm.display, mx->drawGC, clip_region); XDestroyRegion (clip_region); mx->GC_private = 1; if (++dx->nmappings >= MAX_DRAW) break; else mx++; } } dx->valid = 1; } return (dx); } static invalidate_draw_context (w) GtermWidget w; { register DrawContext dx = &w->gterm.draw; register MappingContext mx; register int i; if (dx->valid) { for (i=0; i < dx->nmappings; i++) { mx = &dx->mapContext[i]; if (mx->GC_private) XFreeGC (w->gterm.display, mx->drawGC); } dx->valid = 0; } } static XPoint * mapVector (mx, pv1, pv2, npts) register MappingContext mx; XPoint *pv1; XPoint *pv2; int npts; { register XPoint *ip = pv1; register XPoint *op = pv2; register int n; for (n=npts; --n >= 0; ip++, op++) { op->x = ip->x * mx->xscale + mx->xoffset; op->y = ip->y * mx->yscale + mx->yoffset; } return (pv2); } static void savepos (w, event) GtermWidget w; XEvent *event; { if (event == NULL) return; switch (event->type) { case KeyPress: case KeyRelease: w->gterm.last_x = event->xkey.x; w->gterm.last_y = event->xkey.y; break; case ButtonPress: case ButtonRelease: w->gterm.last_x = event->xbutton.x; w->gterm.last_y = event->xbutton.y; break; case MotionNotify: w->gterm.last_x = event->xmotion.x; w->gterm.last_y = event->xmotion.y; break; } } /* * IMAGING routines. * ----------------------- * Our strategy here is to support a range of visuals with pseudocolor being * preferred if available. All imaging is done internally using 8 bit images * and a max 256 element colormap. If the display hardware has a depth less * than 8 bits, e.g. for a monochrome display, the image is reduced to the * screen depth by some technique before being output to the display. * * Images (rasters) are implemented internally in Gterm using either ximages or * off screen pixmaps. Which format is used is decided at raster create time * and is controlled by a Gterm resource. This is transparent to the client * application. Currently only 8 bit rasters are supported. * * GtRasterInit (gt) * GtAssignRaster (gt, raster, drawable) * GtCreateRaster (gt, raster, type, width, height, depth) * GtDestroyRaster (gt, raster) * exists = GtQueryRaster (gt, raster, &type, &width, &height, &depth) * raster = GtNextRaster (gt) * GtSetRaster (gt, raster) * raster = GtGetRaster (gt) * n = GtNRasters (gt) * * GtWritePixels (gt, raster, pixels, nbits, x1, y1, nx, ny) * GtReadPixels (gt, raster, pixels, nbits, x1, y1, nx, ny) * GtSetPixels (gt, raster, ct, x1, y1, nx, ny, color, rop) * GtRefreshPixels (gt, raster, ct, x1, y1, nx, ny) * pixmap = GtExtractPixmap (gt, src, ct, x, y, width, height) * GtInsertPixmap (gt, pixmap, dst, ct, x, y, width, height) * * colormap = GtNextColormap (gt) * GtFreeColormap (gt, colormap) * GtWriteColormap (gt, colormap, first, nelem, r, g, b) * GtReadColormap (gt, colormap, first, nelem, r, g, b) * GtLoadColormap (gt, colormap, offset, scale) * exists = GtQueryColormap (gt, colormap, &first, &nelem, &maxelem) * GtWriteIomap (gt, iomap, first, nelem) * GtReadIomap (gt, iomap, first, nelem) * pixel = GtGetClientPixel (gt, gterm_pixel) * * GtInitMappings (gt) * mapping = GtNextMapping (gt) * GtFreeMapping (gt, mapping) * GtRaiseMapping (gt, mapping, ref|NULL) * GtLowerMapping (gt, mapping, ref|NULL) * int = GtCompareMappings (gt, map1, map2) * GtEnableMapping (gt, mapping, refresh) * GtDisableMapping (gt, mapping, erase) * active = GtActiveMapping (gt, mapping) * GtRefreshMapping (gt, mapping) * raster = GtSelectRaster (gt, dras, dt, dx, dy, rt, &rx, &ry, &mapping) * * GtCopyRaster (gt, rop, * src,st,sx,sy,snx,sny, dst,dt,dx,dy,dnx,dny) * GtSetMapping (gt, mapping, rop, * src,st,sx,sy,snx,sny, dst,dt,dx,dy,dnx,dny) * GtGetMapping (gt, mapping, rop, * src,st,sx,sy,snx,sny, dst,dt,dx,dy,dnx,dny) * * GtMapVector (gt, mapping, dir, pv1, pv2, npts) * GtPixelToNDC (gt, raster, pv1, pv2, npts) * GtNDCToPixel (gt, raster, pv1, pv2, npts) * * GtDebug (gt, fp, what) * * In the case of CopyRaster or {Set|Get}Mapping, raster coordinates may be * specified in either raster pixel (GtPixel) units or in normalized device * coordinates (GtNDC) in the range 0-32767. * --------------------------------------------------------------------------- */ GtRasterInit (w) GtermWidget w; { register int i; register Raster rp; register struct colormap *cm; struct colormap *next_cm; invalidate_draw_context (w); /* Destroy any existing rasters. */ if (w->gterm.rasters) for (i=1; i < w->gterm.maxRasters; i++) if (w->gterm.rasters[i].type) GtDestroyRaster (w, i); /* Allocate the initially empty raster descriptors. */ XtFree ((char *)w->gterm.rasters); w->gterm.rasters = rp = (Raster) XtCalloc (w->gterm.maxRasters, sizeof (struct raster)); w->gterm.nrasters = 0; w->gterm.raster = 0; /* Raster 0 is the display window. */ rp->type = PixmapRaster; rp->width = w->core.width; rp->height = w->core.height; rp->r.pixmap = w->gterm.window; rp->delete = 0; w->gterm.nrasters++; /* Free any previously allocated colormap cells. */ if (w->gterm.ncolors > SZ_STATIC_CMAP && w->gterm.useDefaultCM) { XFreeColors (w->gterm.display, w->core.colormap, &w->gterm.cmap[SZ_STATIC_CMAP], w->gterm.ncolors - SZ_STATIC_CMAP, 0); w->gterm.ncolors = SZ_STATIC_CMAP; invalidate_cmap (w); } /* Free any client defined colormaps. */ for (cm = w->gterm.colormaps; cm; cm = next_cm) { next_cm = cm->next; XtFree ((char *)cm); } w->gterm.colormaps = NULL; /* Initialize the mappings. */ GtInitMappings (w); } /* GtNextRaster -- Return the index of the next unused raster. */ GtNextRaster (w) register GtermWidget w; { register int i; if (w->gterm.rasters) for (i=1; i < w->gterm.maxRasters; i++) if (!w->gterm.rasters[i].type) return (i); return (-1); } /* GtNRasters -- Return the number of currently defined rasters. */ GtNRasters (w) GtermWidget w; { return (w->gterm.nrasters); } /* GtAssignRaster -- Assign a raster descriptor to an externally created * drawable (window or pixmap). The raster thus created may be mapped or * drawn into like the rasters created privately by the imaging code, but * this allows use of this code to access other windows, or shared pixmaps. */ GtAssignRaster (w, raster, drawable, type) GtermWidget w; int raster; /* one-indexed */ XtPointer drawable; /* object containing pixel array */ int type; /* type of drawable [not used] */ { register Raster rp; XWindowAttributes wa; if (raster <= 0 || raster >= w->gterm.maxRasters) return (ERR); else rp = &w->gterm.rasters[raster]; if (!XGetWindowAttributes (w->gterm.display, (Window)drawable, &wa)) return (ERR); rp->type = PixmapRaster; rp->width = wa.width; rp->height = wa.height; rp->r.pixmap = (Pixmap) drawable; rp->delete = 0; return (OK); } /* GtCreateRaster -- Create a new raster of the given size. A server pixmap * (GtServer) or ximage (GtClient) raster will be created depending upon the * current value of the cacheRasters resource. */ GtCreateRaster (w, raster, type, width, height, depth) GtermWidget w; int raster; /* one-indexed */ int type; int width, height; int depth; { register uchar *op; register int npix, pixel; uchar *data; XImage *xp; Raster rp; int cache; if (!XtIsRealized ((Widget)w)) return (ERR); /* Only rasters of depth 8 bits are currently supported. */ if (depth && depth != 8) return (ERR); /* Check for a raster number in bounds. */ if (raster < 0 || raster >= w->gterm.maxRasters) return (ERR); else rp = &w->gterm.rasters[raster]; /* A create on raster 0 (the display window) is treated as an attempt * to resize the window. */ if (raster == 0) { XWindowAttributes wa; invalidate_draw_context (w); /* Issue the resize request. */ XtVaSetValues ((Widget)w, XtNwidth, (XtArgVal)width, XtNheight, (XtArgVal)height, NULL); XFlush (w->gterm.display); /* The following generates a round trip request to the server and * is an attempt to allow the window system time to process the * resize request before the client can issue a GtQueryRaster to * see if the request has succeeded (hence causing a race condition). * If the window is not the requested size the delay flag is set * to cause graphics input processing to be suspended until the * window is resized or redisplayed. A dummy expose event is * generated to clear the delay condition in case the resize request * is not granted. */ if (XGetWindowAttributes (w->gterm.display, w->gterm.window, &wa)) { rp->width = wa.width; rp->height = wa.height; if (rp->width != width || rp->height != height) { XExposeEvent ev; ev.type = Expose; ev.send_event = True; ev.display = w->gterm.display; ev.window = w->gterm.window; ev.x = ev.y = 0; ev.width = ev.height = 1; ev.count = 0; XSendEvent (w->gterm.display, w->gterm.window, False, NoEventMask, (XEvent *)&ev); w->gterm.delay = 1; } } return (OK); } /* Get rid of any old raster. */ GtDestroyRaster (w, raster); rp->width = width; rp->height = height; rp->delete = 1; /* Cache the raster? */ if (strcmp (w->gterm.cacheRasters, "always") == 0) cache = 1; else if (strcmp (w->gterm.cacheRasters, "never") == 0) cache = 0; else cache = (type == GtServer); /* Create new raster. */ if (cache) { /* Create a pixmap. */ rp->type = PixmapRaster; rp->r.pixmap = XCreatePixmap (w->gterm.display, w->gterm.window, width, height, RasterDepth); if (rp->r.pixmap == (Pixmap)NULL) goto ximage; XFillRectangle (w->gterm.display, rp->r.pixmap, w->gterm.clearGC, 0, 0, width, height); } else { /* Create an XImage. */ ximage: rp->type = ImageRaster; /* Get pixel storage. */ npix = width * height; if ((data = (uchar *) XtMalloc (npix)) == NULL) return (ERR); else { for (op=data, pixel=w->gterm.color0; --npix >= 0; ) *op++ = pixel; } /* The following doesn't yet deal properly with byte and bit ordering * differences between the server and client. */ rp->r.ximage = xp = XCreateImage (w->gterm.display, NULL, RasterDepth, ZPixmap, 0, (char *)data, width, height, 8, 0); if (xp == NULL) { rp->type = 0; return (ERR); } } w->gterm.nrasters++; return (OK); } /* GtDestroyRaster -- Destroy a raster. Any mappings which reference the * raster are deactivated, and all storage associated with the raster is freed. */ GtDestroyRaster (w, raster) GtermWidget w; int raster; { register Raster rp; register Mapping mp, next; if (raster <= 0) return; invalidate_draw_context (w); /* Disable any mappings that reference this raster. */ for (mp = w->gterm.mp_head; mp; mp = next) { next = mp->next; if (mp->src == raster || mp->dst == raster) free_mapping (w, mp); } /* Destroy the raster. */ rp = &w->gterm.rasters[raster]; if (rp->type) { if (rp->delete) { if (rp->type == ImageRaster) XDestroyImage (rp->r.ximage); else if (rp->type == PixmapRaster) XFreePixmap (w->gterm.display, rp->r.pixmap); } w->gterm.nrasters--; rp->type = 0; rp->delete = 0; } } /* GtQueryRaster -- Determine whether a raster exists and if so return its * size. */ GtQueryRaster (w, raster, type, width, height, depth) GtermWidget w; int raster; /* one-indexed */ int *type; int *width, *height; int *depth; { register Raster rp; if (raster < 0 || raster > w->gterm.maxRasters) return (0); rp = &w->gterm.rasters[raster]; if (rp->type) { if (type) { if (rp->type == PixmapRaster) *type = GtServer; else *type = GtClient; } if (width) *width = rp->width; if (height) *height = rp->height; if (depth) *depth = RasterDepth; return (1); } else return (0); } /* GtWritePixels -- Write to a rectangular region of a raster. If any * mappings are currently defined which reference this raster as the source, * and a mapped region is being rewritten, the affected pixels will be * refreshed by the mapping. */ GtWritePixels (w, raster, pixels, nbits, x1, y1, nx, ny) GtermWidget w; int raster; uchar *pixels; int nbits; /* not used */ int x1, y1; int nx, ny; { register uchar *ip, *op; register Pixel *cmap; register int n; int bytes_per_line, i; Mapping mp; Raster rp; uchar *lp; rp = &w->gterm.rasters[raster]; if (rp->type == 0) return (ERR); /* Perform some range checks. */ if (x1 < 0 || x1 > rp->width || nx <= 0 || x1+nx > rp->width) return (ERR); if (y1 < 0 || y1 > rp->height || ny <= 0 || y1+ny > rp->height) return (ERR); if (rp->type == PixmapRaster) { Display *display = w->gterm.display; XImage *ximage; uchar *data; int npix; /* Get a data buffer. */ if ((data = (uchar *)XtMalloc (npix = nx * ny)) == NULL) return (ERR); /* Convert the pixel values to colormap indices. */ cmap = get_cmap_in (w); for (ip=pixels, op=data, n=npix; --n >= 0; ) *op++ = (cmap[*ip++] & 0377); ximage = XCreateImage (w->gterm.display, NULL, RasterDepth, ZPixmap, 0, (char *)data, nx, ny, 8, 0); if (raster == 0 && w->gterm.pixmap) { /* ### Someone (Mike?) added this code for some reason, but it * does not appear to be valid. This code already writes to the * backing store (gterm.pixmap) so use_backing_store should not * be required. Also blindly using only the first mapping context * and ignoring any others cannot be correct. There is code * below which executes any mappings defined on the raster. * In general this requires scaling, not a simple XCopyArea. * * DrawContext dx = get_draw_context (w); * register MappingContext mx; * mx = &dx->mapContext[0]; */ XPutImage (display, w->gterm.pixmap, w->gterm.exposeGC, ximage, 0, 0, x1, y1, nx, ny); /* ### (cont'd) * if (mx->use_backing_store) * XCopyArea (display, w->gterm.pixmap, mx->pixmap, * w->gterm.exposeGC, x1, y1, nx, ny, x1, y1); */ XCopyArea (display, w->gterm.pixmap, rp->r.pixmap, w->gterm.exposeGC, x1, y1, nx, ny, x1, y1); } else XPutImage (display, rp->r.pixmap, w->gterm.exposeGC, ximage, 0, 0, x1, y1, nx, ny); XtFree ((char *)data); ximage->data = NULL; XDestroyImage (ximage); } else if (rp->type == ImageRaster) { cmap = get_cmap_in (w); bytes_per_line = rp->r.ximage->bytes_per_line; lp = (uchar *)rp->r.ximage->data + y1 * bytes_per_line + x1; ip = pixels; /* Copy the data into the ximage data raster, converting input * pixels to Xlib pixel values in the process. * * Possibly this should be done at Pixmap write time rather than * during raster i/o so that the image pixel values are preserved. * Otherwise reading back pixels is difficult and if the color map is * dynamically modified the original pixel values may be lost. * Postponing display pixel value generation woudl also make it easy * to add support later for image depths other than 8 bit. Doing the * conversion to display pixels here is however simpler and more * efficient so that is how we do it for now. */ for (i=0; i < ny; i++) { for (n=nx, op=lp; --n >= 0; ) *op++ = (cmap[*ip++] & 0377); lp += bytes_per_line; } } /* Execute any mappings that reference this raster. */ for (mp = w->gterm.mp_head; mp; mp = mp->next) { if (mp->enabled && mp->src == raster) { struct mapping *map=mp, p_mp; if (map->st != GtPixel || map->dt != GtPixel) { initialize_mapping (&p_mp); get_pixel_mapping (w, map, &p_mp, 1); update_mapping (w, map = &p_mp); } else update_mapping (w, map); refresh_source (w, map, x1, y1, nx, ny); if (map == &p_mp) free_mapping (w, map); } } return (OK); } /* GtReadPixels -- Read a rectangular region of a raster. */ GtReadPixels (w, raster, pixels, nbits, x1, y1, nx, ny) GtermWidget w; int raster; uchar *pixels; int nbits; /* not used */ int x1, y1; int nx, ny; { register uchar *ip, *op; register Pixel *cmap; register int n; int bytes_per_line, i; int x, y, delxin = 0; XImage *xin; Raster rp; uchar *lp; rp = &w->gterm.rasters[raster]; if (rp->type == 0) return (ERR); /* Perform some range checks. */ if (x1 < 0 || x1 > rp->width || nx <= 0 || x1+nx > rp->width) return (ERR); if (y1 < 0 || y1 > rp->height || ny <= 0 || y1+ny > rp->height) return (ERR); /* Get the input ximage. */ if (rp->type == PixmapRaster) { Display *display = w->gterm.display; /* Read the pixmap subraster into an ximage. If we are reading the * screen (raster == 0) and we have an off-screen backing store pixmap, * use that instead of the screen. */ xin = XGetImage (display, (raster == 0 && w->gterm.pixmap) ? w->gterm.pixmap : rp->r.pixmap, x1, y1, nx, ny, 0xff, ZPixmap); if (xin == NULL) return (ERR); delxin++; x = y = 0; } else { xin = rp->r.ximage; x = x1; y = y1; } cmap = get_cmap_out (w); bytes_per_line = xin->bytes_per_line; lp = (uchar *)xin->data + y * bytes_per_line + x; op = pixels; /* Copy the data to the output buffer, converting display pixels to * client pixels in the process. */ for (i=0; i < ny; i++) { for (n=nx, ip=lp; --n >= 0; ) *op++ = cmap[*ip++]; lp += bytes_per_line; } if (delxin) XDestroyImage (xin); return (OK); } /* GtSetPixels -- Set all the raster pixels in a region to a single color. * If nx=ny=0 the entire raster will be written. */ GtSetPixels (w, raster, ct, x1, y1, nx, ny, color, rop) GtermWidget w; int raster; int ct; int x1, y1; int nx, ny; int color; int rop; { register Raster rp; Mapping mp; /* Get raster pointer. */ rp = &w->gterm.rasters[raster]; if (rp->type == 0) return (ERR); /* Get pixel coordinates. */ if (ct != GtPixel) { struct mapping sv_mp, p_mp; save_mapping (&sv_mp, 0, 0, 0, GtPixel, 0,0,0,0, raster, ct, x1,y1,nx,ny); get_pixel_mapping (w, &sv_mp, &p_mp, 0); x1 = p_mp.dx; y1 = p_mp.dy; nx = p_mp.dnx; ny = p_mp.dny; } /* Perform some range checks. */ if (x1 == 0 && y1 == 0 && nx == 0 && ny == 0) { nx = rp->width; ny = rp->height; } else { if (x1 < 0 || x1 > rp->width || nx <= 0 || x1+nx > rp->width) return (ERR); if (y1 < 0 || y1 > rp->height || ny <= 0 || y1+ny > rp->height) return (ERR); } /* Set the pixels. */ if (rp->type == PixmapRaster) { Display *display = w->gterm.display; GC gc = w->gterm.clearGC; int use_backing_store; use_backing_store = (raster == 0 && w->gterm.pixmap && !(rop & R_Transient)); XSetForeground (display, gc, get_pixel(w,color)); XFillRectangle (display, rp->r.pixmap, gc, x1, y1, nx, ny); if (use_backing_store) XFillRectangle (display, w->gterm.pixmap, gc, x1, y1, nx, ny); XSetForeground (display, gc, w->gterm.color0); } else { register int n, i; register uchar *op; register Pixel pixel; int bytes_per_line; uchar *lp; pixel = get_pixel (w, color); bytes_per_line = rp->r.ximage->bytes_per_line; lp = (uchar *)rp->r.ximage->data + y1 * bytes_per_line + x1; /* Set all pixels in the indicated region. */ for (i=0; i < ny; i++) { for (n=nx, op=lp; --n >= 0; ) *op++ = pixel; lp += bytes_per_line; } } /* Execute any mappings that reference this raster. */ for (mp = w->gterm.mp_head; mp; mp = mp->next) { if (mp->enabled && mp->src == raster) { struct mapping *map=mp, p_mp; if (map->st != GtPixel || map->dt != GtPixel) { initialize_mapping (&p_mp); get_pixel_mapping (w, map, &p_mp, 1); update_mapping (w, map = &p_mp); } else update_mapping (w, map); refresh_source (w, map, x1, y1, nx, ny); if (map == &p_mp) free_mapping (w, map); } } return (OK); } /* GtRefreshPixels -- Update any mappings defined upon the given region of * the given source raster, as if the pixel values had been set with a * write pixels call. */ GtRefreshPixels (w, raster, ct, x1, y1, nx, ny) GtermWidget w; int raster; int ct; int x1, y1; int nx, ny; { register Raster rp = &w->gterm.rasters[raster]; register Mapping mp; if (!XtIsRealized ((Widget)w)) return; /* Get pixel coordinates. */ if (ct != GtPixel) { struct mapping sv_mp, p_mp; initialize_mapping (&sv_mp); save_mapping (&sv_mp, 0, 0, raster, ct, x1,y1,nx,ny, 0, GtPixel, 0,0,0,0); get_pixel_mapping (w, &sv_mp, &p_mp, 0); x1 = p_mp.sx; y1 = p_mp.sy; nx = p_mp.snx; ny = p_mp.sny; } /* Execute any mappings that reference this raster. */ for (mp = w->gterm.mp_head; mp; mp = mp->next) { if (mp->enabled && mp->src == raster) { struct mapping *map=mp, p_mp; if (map->st != GtPixel || map->dt != GtPixel) { initialize_mapping (&p_mp); get_pixel_mapping (w, map, &p_mp, 1); update_mapping (w, map = &p_mp); } else update_mapping (w, map); refresh_source (w, map, x1, y1, nx, ny); if (map == &p_mp) free_mapping (w, map); } } } /* GtExtractPixmap -- Extract a rectangular region of a raster and return * as a pixmap. The caller is responsible for later deleting this pixmap. */ Pixmap GtExtractPixmap (w, src, ctype, x, y, width, height) GtermWidget w; int src; int ctype; int x, y; int width, height; { register Raster rp; int x1, y1, nx, ny; String cache; int i; rp = &w->gterm.rasters[src]; if (!rp->type) return ((Pixmap)NULL); /* If width and height are zero, return the full raster. */ if (width <= 0) width = rp->width; if (height <= 0) height = rp->height; /* Get pixel coordinates. */ if (ctype != GtPixel) { struct mapping sv_mp, p_mp; save_mapping (&sv_mp, 0, 0, 0, GtPixel, 0,0,0,0, src, ctype, x,y,width,height); get_pixel_mapping (w, &sv_mp, &p_mp, 0); x1 = p_mp.dx; y1 = p_mp.dy; nx = p_mp.dnx; ny = p_mp.dny; } else { x1 = x; y1 = y; nx = width; ny = height; } /* Find any empty raster slot and use it to generate the output pixmap. */ for (i=0; i < w->gterm.maxRasters; i++) { rp = &w->gterm.rasters[i]; if (!rp->type) { cache = w->gterm.cacheRasters; w->gterm.cacheRasters = "always"; if (GtCreateRaster (w, i, GtServer, nx, ny) == ERR) { w->gterm.cacheRasters = cache; return ((Pixmap)NULL); } else if (rp->type != PixmapRaster) goto err; if (GtCopyRaster (w, 0, src,0, x1,y1,nx,ny, i,0, 0,0,nx,ny) == ERR) { err: GtDestroyRaster (i); w->gterm.cacheRasters = cache; return ((Pixmap)NULL); } rp->type = 0; w->gterm.nrasters--; w->gterm.cacheRasters = cache; return (rp->r.pixmap); } } return ((Pixmap)NULL); } /* GtInsertPixmap -- Insert the contents of the given pixmap into a raster * at the indicated coordinates. */ GtInsertPixmap (w, pixmap, dst, ctype, x, y, width, height) GtermWidget w; Pixmap pixmap; int dst; int ctype; int x, y; int width, height; { register Raster rp; XWindowAttributes wa; int x1, y1, nx, ny; int i; /* Check that the pixmap exists. */ if (!XGetWindowAttributes (w->gterm.display, pixmap, &wa)) return (ERR); /* Default to full dimensions of pixmap if no dimensions given. */ if (width <= 0) width = wa.width; if (height <= 0) height = wa.height; /* Get pixel coordinates. */ if (ctype != GtPixel) { struct mapping sv_mp, p_mp; save_mapping (&sv_mp, 0, 0, 0, GtPixel, 0,0,0,0, dst, ctype, x,y,width,height); get_pixel_mapping (w, &sv_mp, &p_mp, 0); x1 = p_mp.dx; y1 = p_mp.dy; nx = p_mp.dnx; ny = p_mp.dny; } else { x1 = x; y1 = y; nx = width; ny = height; } /* Create the destination raster if none exists. */ if (!w->gterm.rasters[dst].type) if (GtCreateRaster (w, dst, GtDefault, nx, ny) == ERR) return (ERR); /* Find an empty raster slot and use it to build a fake source raster * using the supplied pixmap. */ for (i=0; i < w->gterm.maxRasters; i++) { rp = &w->gterm.rasters[i]; if (!rp->type) { rp->type = PixmapRaster; rp->width = nx; rp->height = ny; rp->r.pixmap = pixmap; if (GtCopyRaster (w, 0, i,0, 0,0,nx,ny, dst,0, x1,y1,nx,ny) < 0) return (ERR); rp->type = 0; return (OK); } } return (ERR); } /* GtWriteColormap -- Allocate or modify colormap cells. The Gterm widget * colormap consists of a fixed number of preassigned, read-only color cells, * plus a variable number of dynamically allocated application defined color * cells. The application sees only the preassigned pixel space 0-N where * N is the maximum pixel value. These values are mapped to Xlib pixel values * by the CMAP vector in the main Gterm widget descriptor. The server * colormap does the final mapping to RGB triplets. * * Our strategy here is as follows. If we have a monochrome screen set up a * one-to-one fake colormap so that we preserve the input pixel values and * render a one-bit image later. If we have a StaticGray or StaticColor * visual allocate read-only color cells to allow X to choose the closest * colors to what we request. If we have a GrayScale or PseudoColor visual * allocate private read-write colors. The TrueColor and DirectColor * visuals should not be seen here as we should have been able to set up the * PseudoColor visual on screens that support these visuals, but if they are * seen use a one-to-one mapping to preserve the 8 bit pixel values. */ GtWriteColormap (w, map, first, nelem, r, g, b) GtermWidget w; int map; int first; int nelem; ushort *r, *g, *b; { XWindowAttributes wa; register XColor *cp; register int i, j, v, n; unsigned long plane_masks[1]; int req, need; if (!XtIsRealized ((Widget)w)) return (ERR); if (map > 0) { /* Create or modify a colormap descriptor. The display colormap * is not affected. */ register struct colormap *cm; struct colormap *last_cm; register XColor *cp; register int i; /* See if the colormap already exists. */ for (cm = w->gterm.colormaps, last_cm = NULL; cm; cm = cm->next) { last_cm = cm; if (cm->map == map) break; } /* If not, create it. */ if (!cm) { if (!(cm = (struct colormap *)XtCalloc(1,sizeof(struct colormap)))) return (ERR); if (last_cm) last_cm->next = cm; else w->gterm.colormaps = cm; /* Initialize static part of colormap. */ for (i=0; i < SZ_STATIC_CMAP; i++) { cp = &w->gterm.color[i]; cm->r[i] = cp->red; cm->g[i] = cp->green; cm->b[i] = cp->blue; } } cm->map = map; cm->ncells = max (cm->ncells, first + nelem); /* Ignore attempts to overwrite static part of colormap. */ for ( ; first < SZ_STATIC_CMAP; first++, nelem--) { r++; g++; b++; } memmove (&cm->r[first], r, nelem * sizeof (ushort)); memmove (&cm->g[first], g, nelem * sizeof (ushort)); memmove (&cm->b[first], b, nelem * sizeof (ushort)); return (OK); } /* Write to the display colormap. */ if (first < SZ_STATIC_CMAP || first + nelem > MAX_SZCMAP) return (ERR); /* Invalidate the cmap cache. */ invalidate_cmap (w); /* Get the window attributes. We need to do this to determine the type * of visual used for the window, which determines our color allocation * strategy. This is only done once since presumably the visual and * window depth will not change after the widget has been around long * enough to receive a GtWriteColormap call. */ if (!w->gterm.wa_defined) { if (!XGetWindowAttributes (w->gterm.display, w->gterm.window, &wa)) return (ERR); w->gterm.wa = wa; w->gterm.wa_defined++; } else wa = w->gterm.wa; if (wa.depth == 1) goto unitary; switch (wa.visual->class) { case StaticGray: case StaticColor: /* Allocate "best-match" colors. */ for (i=first; i < first+nelem; i++) { cp = &w->gterm.color[i]; cp->red = r[i-first]; cp->green = g[i-first]; cp->blue = b[i-first]; cp->flags = (DoRed | DoGreen | DoBlue); if (XAllocColor (w->gterm.display, wa.colormap, cp)) w->gterm.cmap[i] = cp->pixel; else { w->gterm.cmap[i] = cp->pixel = BlackPixelOfScreen (w->gterm.screen); } } break; case GrayScale: case PseudoColor: if (w->gterm.useDefaultCM) { usedef: /* Allocate private r/w colors from default colormap. */ need = first + nelem - w->gterm.ncolors; /* Allocate new color cells if needed. If we can't allocate all * the requested cells the unallocated pixel values are set to * black. */ if ((n = need) > 0) { /* Get the colormap cells. */ req = min(w->gterm.maxColors, first + nelem - SZ_STATIC_CMAP) - (w->gterm.ncolors - SZ_STATIC_CMAP); for (n=0; req > 0 && n < need; ) if (XAllocColorCells (w->gterm.display, wa.colormap, False, plane_masks, 0, &w->gterm.cmap[w->gterm.ncolors+n], req)) { n += req; } else req /= 2; /* Initialize the color descriptors. */ for (i = w->gterm.ncolors; i < first+nelem; i++) { cp = &w->gterm.color[i]; if (i < w->gterm.ncolors + n) { cp->pixel = w->gterm.cmap[i]; cp->flags = (DoRed | DoGreen | DoBlue); } else { w->gterm.cmap[i] = cp->pixel = BlackPixelOfScreen (w->gterm.screen); cp->flags = 0; } } w->gterm.ncolors += n; } /* Assign RGB colors to the referenced cells. */ for (i=0; i < nelem; i++) { cp = &w->gterm.color[first+i]; cp->red = r[i]; cp->green = g[i]; cp->blue = b[i]; } n = w->gterm.ncolors - first; if (n > 0) XStoreColors (w->gterm.display, wa.colormap, &w->gterm.color[first], n); } else { /* Allocate colors in a custom colormap. If the named colormap * does not yet exist we create one. Multiple gterm widget * instances may share the same colormap. */ Colormap colormap; long timeval, time(); int ncolors, shadow; /* get_colormap will direct us to the default colormap if the * custom colormap cannot be accessed or created. */ colormap = get_colormap (w); if (w->gterm.useDefaultCM) goto usedef; /* Assign RGB colors to the referenced cells. */ ncolors = min (w->gterm.maxColors, nelem); cp = &w->gterm.color[first]; for (i=0; i < ncolors; i++, cp++) { cp->flags = (DoRed | DoGreen | DoBlue); cp->red = r[i]; cp->green = g[i]; cp->blue = b[i]; } /* Store the new colors. */ if (ncolors > 0) XStoreColors (w->gterm.display, colormap, &w->gterm.color[first], ncolors); /* Also attempt to store the colors in the default colortable, * by allocating, writing, and then deallocating cells. This * helps keeps the gterm window visible when the default * colormap is loaded. To avoid excessive server queries the * default colormap is only updated every so often. Updating is * disabled if cmapShadow is set to zero. If shadowing is * enabled the update is always performed if the WriteColormap * occurs when the pointer is not in the window (e.g., when a * button elsewhere in the GUI is pressed) as otherwise the * change will not be visible as the private colormap will not * be loaded by the window manager. */ shadow = w->gterm.cmapShadow; timeval = time((long *)NULL); if (shadow && (!w->gterm.in_window || (timeval - w->gterm.cmapLastShadow > shadow))) { update_default_colormap (w); w->gterm.cmapLastShadow = timeval; } } break; default: /* Set up a unitary, or one-to-one mapping, to preserve the input * pixel values so that we can render them later. */ unitary: for (i = first; i < first+nelem; i++) { w->gterm.cmap[i] = i; cp = &w->gterm.color[i]; cp->pixel = i; cp->red = r[i+first]; cp->green = g[i+first]; cp->blue = b[i+first]; cp->flags = (DoRed | DoGreen | DoBlue); if (i+1 > w->gterm.ncolors) w->gterm.ncolors = i + 1; } break; } return (OK); } /* GtReadColormap -- Return the color assignments for a region of the named * colormap. */ GtReadColormap (w, map, first, nelem, r, g, b) GtermWidget w; int map; int first; int nelem; ushort *r, *g, *b; { if (map > 0) { /* Read from a colormap descriptor. */ register struct colormap *cm; register int i, j; /* Locate colormap. */ for (cm = w->gterm.colormaps; cm; cm = cm->next) if (cm->map == map) break; if (!cm) return (0); /* Return RGB values. */ for (i=0; i < nelem; i++) { j = first + i; if (j < cm->ncells) { r[i] = cm->r[j]; g[i] = cm->g[j]; b[i] = cm->b[j]; } else break; } return (i); } else { /* Read the display colormap. */ register XColor *cp; register int i; /* Return RGB values. */ for (i=0; i < nelem; i++) if (first+i < w->gterm.ncolors) { cp = &w->gterm.color[first+i]; r[i] = cp->red; g[i] = cp->green; b[i] = cp->blue; } else break; return (i); } } /* GtLoadColormap -- Load a colormap into the display, optionally scaling * the colormap via a linear transformation in the process. The colormap is * unaffected if offset=0.5, scale=1.0. A negative scale inverts the image. * If map=0 the linear transformation is applied directly to the display * colormap. * * The offset refers to the center of the mapped region of the transfer * function, which is why the center value is at 0.5. For example, if the * range of raster pixel intensities is normalized to the range 0.0 to 1.0, * then a transfer function of [offset=0.3,slope=3.0] will display the region * of intenstities centered around the normalized intenstity of 0.3, with a * contrast of 3.0 (the screen intensity changes 3 units for a unit change in * raster pixel intensity). The transfer function [offset=0.3,slope=-3.0] * will display the same range of pixel intensitites, but with a negative * contrast. The transfer function [offset=0.5,slope=1.0] has intercepts * of [0,0] and [1,1] hence it displays the full range of raster pixel * intensities - the input colormap is used as is, without resampling. */ GtLoadColormap (w, map, offset, slope) GtermWidget w; int map; float offset, slope; { register int i; register XColor *cp; register struct colormap *cm; struct colormap d_cmap, o_cmap; int noscale, nelem, c1, c2; float x, y, z, frac; ushort r, g, b; /* Get the colormap to be loaded. */ if (map == 0) { /* Create a dummy colormap struct from the screen colormap. */ cm = &d_cmap; cm->map = 0; cm->next = NULL; cm->ncells = w->gterm.ncolors; for (i=0; i < cm->ncells; i++) { cp = &w->gterm.color[i]; cm->r[i] = cp->red; cm->g[i] = cp->green; cm->b[i] = cp->blue; } } else { /* Locate colormap. */ for (cm = w->gterm.colormaps; cm; cm = cm->next) if (cm->map == map) break; if (!cm) return (ERR); } /* Compute the scaled colormap. Only the dynamic part of the colormap * is scaled, the static cells are not affected. */ o_cmap.map = 0; o_cmap.ncells = cm->ncells; nelem = cm->ncells - SZ_STATIC_CMAP; noscale = (abs(offset - 0.5) < 0.0001 && abs(slope - 1.0) < 0.0001); if (noscale) { for (i=0; i < nelem; i++) { o_cmap.r[i] = cm->r[SZ_STATIC_CMAP+i]; o_cmap.g[i] = cm->g[SZ_STATIC_CMAP+i]; o_cmap.b[i] = cm->b[SZ_STATIC_CMAP+i]; } } else { for (i=0; i < nelem; i++) { x = (float)i / (float)(nelem - 1); y = (x - offset) * slope + 0.5; if (y <= 0.0) { r = cm->r[SZ_STATIC_CMAP]; g = cm->g[SZ_STATIC_CMAP]; b = cm->b[SZ_STATIC_CMAP]; } else if (y >= 1.0) { r = cm->r[cm->ncells-1]; g = cm->g[cm->ncells-1]; b = cm->b[cm->ncells-1]; } else { z = y * (nelem - 1) + SZ_STATIC_CMAP; if (w->gterm.cmapInterpolate) { c1 = (int)z; c2 = min (cm->ncells-1, c1 + 1); frac = z - c1; r = cm->r[c1] * (1.0 - frac) + cm->r[c2] * frac; g = cm->g[c1] * (1.0 - frac) + cm->g[c2] * frac; b = cm->b[c1] * (1.0 - frac) + cm->b[c2] * frac; } else { c1 = (int)z; r = cm->r[c1]; g = cm->g[c1]; b = cm->b[c1]; } } o_cmap.r[i] = r; o_cmap.g[i] = g; o_cmap.b[i] = b; } } /* Load the colormap into the display. */ GtWriteColormap (w, 0, SZ_STATIC_CMAP, nelem, o_cmap.r, o_cmap.g, o_cmap.b); /* If the colormap we loaded to the display was the display colormap, * restore the original unscaled colors in the gterm descriptor so that * we won't be scaling a scaled colormap in the next operation. */ if (map == 0) for (i=SZ_STATIC_CMAP; i < cm->ncells; i++) { cp = &w->gterm.color[i]; cp->red = cm->r[i]; cp->green = cm->g[i]; cp->blue = cm->b[i]; } return (OK); } /* GtQueryColormap -- Return information on the size and state of a colormap. */ GtQueryColormap (w, map, first, nelem, maxelem) register GtermWidget w; int map; int *first, *nelem, *maxelem; { register struct colormap *cm; int nitems; if (first) *first = SZ_STATIC_CMAP; if (nelem) *nelem = 0; if (maxelem) *maxelem = min (w->gterm.maxColors, MAX_SZCMAP) - SZ_STATIC_CMAP; if (map > 0) { for (cm = w->gterm.colormaps; cm; cm = cm->next) if (cm->map == map) break; if (!cm) return (0); if (nelem) *nelem = cm->ncells - SZ_STATIC_CMAP; } else { if (nelem) *nelem = w->gterm.ncolors - SZ_STATIC_CMAP; if (maxelem) { nitems = min (MAX_SZCMAP, CellsOfScreen(w->gterm.screen)); *maxelem = min (nitems, min (w->gterm.maxColors, MAX_SZCMAP - w->gterm.base_pixel)); } } return (1); } /* GtNextColormap -- Return a unique colormap number. */ GtNextColormap (w) register GtermWidget w; { register struct colormap *cm; register int mapno = 0; /* Get the next map number. */ for (cm = w->gterm.colormaps; cm; cm = cm->next) if (cm->map > mapno) mapno = cm->map; return (mapno + 1); } /* GtFreeColormap -- Free a colormap descriptor. */ GtFreeColormap (w, colormap) register GtermWidget w; int colormap; { register struct colormap *p_cm, *cm; /* Find the colormap and free it. */ for (p_cm = NULL, cm = w->gterm.colormaps; cm; p_cm = cm, cm = cm->next) if (cm->map == colormap) { if (p_cm) p_cm->next = cm->next; else w->gterm.colormaps = cm->next; XtFree ((char *)cm); return; } } /* GtWriteIomap -- An iomap is an optional lookup table used to isolate the * client application from the color model used within the Gterm widget. * To simplify color allocation the Gterm widget defines a logical color * space where color 0 is the background, 1 the foreground, 2-N are statically * allocated standard colors, and colors N+1 and above are dynamically * allocated by the graphics application. Less-demanding applications use * only the statically allocated, shared colors. The widget internally maps * these logical colors to whatever the window system requires, but providing * a well-defined logical color space isolates the client from the details of * color allocation in the underlying window system. * * An iomap can be used to define a mapping between the color model of the * client application and the Gterm color model (when we say color model here * we mean color allocation schemes for 8 bit pseudocolor). By default the * iomap is one-to-one. The use of an iomap frees the client from having to * worry about color index translations, and allows color tables to be * combined in the widget for greater efficiency when color tables are serially * applied. The iomap applies to all color indices or pixel values passed * in i/o operations between the client and the Gterm widget. */ GtWriteIomap (w, iomap, first, nelem) register GtermWidget w; ushort *iomap; int first, nelem; { register int c1, c2; c1 = max(0, min(MAX_SZCMAP-1, first)); c2 = max(0, min(MAX_SZCMAP-1, first + nelem - 1)); nelem = c2 - c1 + 1; memmove (&w->gterm.iomap[c1], iomap, nelem * sizeof(ushort)); invalidate_cmap (w); } /* GtReadIomap -- Read back the contents of the iomap. */ GtReadIomap (w, iomap, first, nelem) register GtermWidget w; uchar *iomap; int first, nelem; { register int c1, c2; c1 = max(0, min(MAX_SZCMAP-1, first)); c2 = max(0, min(MAX_SZCMAP-1, first + nelem - 1)); nelem = c2 - c1 + 1; memmove (iomap, &w->gterm.iomap[c1], nelem * sizeof(ushort)); } /* init_iomap -- Initialize the iomap and the cmap cache. */ static void init_iomap (w) GtermWidget w; { register ushort *iomap = w->gterm.iomap; register int i; for (i=0; i < MAX_SZCMAP; i++) iomap[i] = i; invalidate_cmap (w); } /* invalidate_cmap -- Invalidate the cmap cache. */ static void invalidate_cmap (w) register GtermWidget w; { w->gterm.cmap_in_valid = w->gterm.cmap_out_valid = 0; } /* get_cmap_in -- Get the combined input colormap, used to transform color * values received from the client to window system color indices. */ static Pixel * get_cmap_in (w) register GtermWidget w; { register Pixel *cmap, *cmap_in = w->gterm.cmap_in; register ushort *iomap; register int i, j; int ncolors; if (w->gterm.cmap_in_valid) return (cmap_in); cmap = w->gterm.cmap; iomap = w->gterm.iomap; ncolors = w->gterm.ncolors - SZ_STATIC_CMAP; /* If ncolors is small wrap around so that pixel values stay within * the mapped range of output pixels. */ for (i=0; i < MAX_SZCMAP; i++) { j = iomap[i]; if (j > SZ_STATIC_CMAP && ncolors) j = ((j - SZ_STATIC_CMAP) % ncolors) + SZ_STATIC_CMAP; cmap_in[i] = cmap[j]; } w->gterm.cmap_in_valid++; return (cmap_in); } /* get_cmap_out -- Get the combined output colormap, used to transform window * system color indices to the color system of the client. Note that this is * not necessarily a uniquely defined invertible transformation. */ static Pixel * get_cmap_out (w) GtermWidget w; { register Pixel *cmap; register ushort *iomap; Pixel *cmap_out = w->gterm.cmap_out; register int pixel, i; int j; if (w->gterm.cmap_out_valid) return (cmap_out); cmap = w->gterm.cmap; iomap = w->gterm.iomap; /* Invert the two colormaps. This is not very efficient, but we don't * have to do this very often (a GtReadPixels call is about the only * case, and even then the map is cached). */ for (j=0; j < MAX_SZCMAP; j++) { pixel = j; /* Lookup display pixel in cmap. */ for (i=0; i < MAX_SZCMAP; i++) if (cmap[i] == pixel) { pixel = i; break; } if (i >= MAX_SZCMAP) { cmap_out[j] = 0; continue; } /* Lookup cmap pixel in iomap. */ if (iomap[pixel] != pixel) { for (i=0; i < MAX_SZCMAP; i++) if (iomap[i] == pixel) { pixel = i; break; } if (i >= MAX_SZCMAP) { cmap_out[j] = 0; continue; } } cmap_out[j] = pixel; } w->gterm.cmap_out_valid++; return (cmap_out); } /* get_pixel -- Convert a client color index into a display pixel. */ static Pixel get_pixel (w, client_pixel) register GtermWidget w; register int client_pixel; { register Pixel *cmap = get_cmap_in (w); if (client_pixel < 0 || client_pixel >= MAX_SZCMAP) return (w->gterm.cmap[1]); else return (cmap[client_pixel]); } /* GtGetClientPixel -- Convert a gterm pixel into a client pixel. */ GtGetClientPixel (w, pixel) GtermWidget w; register int pixel; { register int i; register ushort *iomap; int client_pixel = 0; get_cmap_in (w); iomap = w->gterm.iomap; for (i=0; i < MAX_SZCMAP; i++) if (iomap[i] == pixel) { client_pixel = i; break; } return (client_pixel); } /* GtInitMappings -- Delete all mappings and initialize the mapping subsystem. */ GtInitMappings (w) register GtermWidget w; { register Mapping mp; register int i; invalidate_draw_context (w); /* Free any mapping storage. */ if (w->gterm.mappings) { for (i=0; i < w->gterm.maxMappings; i++) { mp = &w->gterm.mappings[i]; if (mp->defined) free_mapping (w, mp); } XtFree ((char *)w->gterm.mappings); w->gterm.mp_head = NULL; w->gterm.mp_tail = NULL; } /* Allocate the initially empty mapping descriptors. */ w->gterm.mappings = (Mapping) XtCalloc (w->gterm.maxMappings, sizeof (struct mapping)); for (i=0; i < w->gterm.maxMappings; i++) { mp = &w->gterm.mappings[i]; mp->mapping = i; } w->gterm.nmappings = 0; } /* GtNextMapping -- Return the index of the next available mapping descriptor. * This routine always returns a mapping index of 1 or higher. */ GtNextMapping (w) register GtermWidget w; { register Mapping mp; register int i; for (i=1; i < w->gterm.maxMappings; i++) { mp = &w->gterm.mappings[i]; if (!mp->defined) return (i); } return (-1); } /* GtFreeMapping -- Free a mapping descriptor. */ GtFreeMapping (w, mapping) register GtermWidget w; int mapping; { free_mapping (w, &w->gterm.mappings[mapping]); } /* GtRaiseMapping -- Set the stacking order of a mapping to one level * higher than the reference mapping. If no reference mapping is given * the mapping is raised to the top of the stacking order. */ GtRaiseMapping (w, mapping, reference) register GtermWidget w; int mapping, reference; { register Mapping mp, ref_mp; mp = &w->gterm.mappings[mapping]; if (!mp->defined) return; if (reference <= 0 || reference >= w->gterm.maxMappings) ref_mp = w->gterm.mp_tail; else ref_mp = &w->gterm.mappings[reference]; /* Already on top? */ if (mp == w->gterm.mp_tail) return; mp_unlink (w, mp); mp_linkafter (w, mp, ref_mp); } /* GtLowerMapping -- Change the stacking order of a mapping relative to another * mapping, causing the first mapping to be drawn below the second. */ GtLowerMapping (w, mapping, reference) register GtermWidget w; int mapping, reference; { register Mapping mp, ref_mp; mp = &w->gterm.mappings[mapping]; if (!mp->defined) return; if (reference <= 0 || reference >= w->gterm.maxMappings) ref_mp = NULL; else ref_mp = &w->gterm.mappings[reference]; /* Already lowered? */ if (mp == w->gterm.mp_head) return; /* Lower it. */ mp_unlink (w, mp); if (ref_mp && ref_mp->prev) mp_linkafter (w, mp, ref_mp->prev); else { mp->next = w->gterm.mp_head; w->gterm.mp_head = mp; if (mp->next) mp->next->prev = mp; if (!w->gterm.mp_tail) w->gterm.mp_tail = mp; } } /* GtCompareMappings -- Compare the stacking order of two mappings. A * negative value is returned if the m1 < m2, zero is returned if the * mappings are the same, and a positive value is returned if m1 > m2. */ GtCompareMappings (w, map1, map2) register GtermWidget w; int map1, map2; { register Mapping mp, mp1, mp2; if (map1 == map2) return (0); mp1 = &w->gterm.mappings[map1]; mp2 = &w->gterm.mappings[map2]; for (mp = w->gterm.mp_head; mp; mp = mp->next) if (mp == mp1) return (-1); else if (mp == mp2) return (1); return (0); } /* GtSelectRaster -- Select the raster which maps to the given raster pixel, * and transform the coordinates back to the source raster. The raster number * and the raster coordinates of the source raster are returned. If no raster * maps to the given pixel, raster=src and source raster coordinates are * returned. * * The raster pixel coordinate system is best explained by an example. * Suppose we have a 10x10 raster mapped into a 500x500 window. The * window pixel 0 on an axis has raster pixel coordinate 0.0; pixel 500 * (which is outside the window) has raster pixel coordinate 10.0. The * coordinates correspond to the edge of the pixel as displayed in the * window, i.e., the left edge of the (nonflipped) window is at x=0.0, and * the right edge at x=10.0. Due to the pixelization of the screen, the * maximum value is a limit which is only approached as the magnification * increases. * * The client application may have a different coordinate system than the * above. For example, if the client wants an integer pixel value to be * at the center of a pixel, the first pixel has the coordinate [1,1], and * the raster is 10 pixels wide, the client coordinate system would range * from 0.5 to 10.5 at the edges of the NDC space. */ GtSelectRaster (w, dras, dt, dx, dy, rt, rx, ry, rmap) GtermWidget w; int dras; /* display raster */ int dt; /* coordinate type of input coords */ int dx, dy; /* display raster coordinates */ int rt; /* coordinate type for output */ int *rx, *ry; /* raster coordinates (output) */ int *rmap; /* mapping selected */ { register Mapping mp; float x, y, x2, y2; int raster, mapping; /* Get display raster pixel coordinates. */ if (dt != GtPixel) { struct mapping sv_mp, p_mp; save_mapping (&sv_mp, 0, 0, 0, 0, 0,0,0,0, 0, dt, dx,dy,0,0); get_pixel_mapping (w, &sv_mp, &p_mp, 0); dx = p_mp.dx; dy = p_mp.dy; } /* Search for a mapping which maps to this pixel. The mapping we are * looking for is the mapping closest to the tail of the mapping list * (highest stacking order) which is defined and enabled and which * includes the given display raster pixel in its destination rect. */ for (mp = w->gterm.mp_tail, mapping = -1; mp; mp = mp->prev) { if (mp->defined && mp->enabled && mp->dst == dras) { struct mapping *map, p_mp; int dnx, dny; get_pixel_mapping (w, mp, &p_mp, 0); map = &p_mp; if ((dnx = map->dnx) < 0) dnx = -dnx; if ((dny = map->dny) < 0) dny = -dny; /* Is display raster pixel in destination rect for this mapping? */ if (dnx > 0 && dx >= map->dx && dx < map->dx + dnx && dny > 0 && dy >= map->dy && dy < map->dy + dny) { /* Compute offset into destination rect and apply axis flip * if any from mapping. */ x = dx - map->dx + 0.5; if (map->dnx < 0) x = dnx - x; y = dy - map->dy + 0.5; if (map->dny < 0) y = dny - y; /* Compute the source raster coordinates corresponding to * the given display pixel. This is done in floating point * to permit fractional pixel resolution if the mapping * zooms the raster. */ x = x * (float)map->snx / (float)dnx + map->sx; y = y * (float)map->sny / (float)dny + map->sy; mapping = map->mapping; raster = map->src; x2 = w->gterm.rasters[raster].width; y2 = w->gterm.rasters[raster].height; break; } } } /* Return display raster coordinates if no mapped raster was found. */ if (mapping < 0) { x = dx; y = dy; x2 = (int)w->core.width - 1; y2 = (int)w->core.height - 1; raster = dras; } /* Output coordinates of the requested type. The increased resolution * of NDC coordinates allows fractional pixel coordinates to be returned * (e.g. 1/32 of a pixel for a 1K raster). */ if (rt == GtPixel) { *rx = x; *ry = y; } else { *rx = ( x) / x2 * MAXNDC; *ry = (y2 - y) / y2 * MAXNDC; /* NDC is flipped in Y */ } *rmap = mapping; return (raster); } /* GtCopyRaster -- Copy a region of the source raster to a region of the * destination raster. If the input and output regions are not the same * size the subimage is automatically scaled to fit the destination region. * If the destination extent DNX or DNY is negative, the image is flipped in * that axis. The type of spatial scaling performed is determined by the * scale factors (zoom, dezoom, or no scaling). The rasterop argument is * used to exercise fine control over how the mapping is performed, e.g., to * force a refresh, implement a transient mapping, or in the case of a dezoom * (many-to-one) mapping, select the antialiasing technique to be used. */ GtCopyRaster (w, rop, src,st,sx,sy,snx,sny, dst,dt,dx,dy,dnx,dny) GtermWidget w; int rop; /* rasterop */ int src; /* 0=window, >0 = raster number */ int st; /* coordinate type for source raster */ int sx,sy,snx,sny; /* source raster */ int dst; /* 0=window, >0 = raster number */ int dt; /* coordinate type for destination raster */ int dx,dy,dnx,dny; /* destination raster */ { Mapping sv_mp, p_mp; int status; if (!XtIsRealized ((Widget)w)) return (OK); /* Construct a temporary mapping describing the desired raster copy. */ save_mapping (&sv_mp, w->gterm.maxMappings, 0, src,st,sx,sy,snx,sny, dst,dt,dx,dy,dnx,dny); initialize_mapping (&p_mp); get_pixel_mapping (w, &sv_mp, &p_mp, 1); update_mapping (w, &p_mp); /* Refresh the destination pixels. */ status = refresh_destination (w, &p_mp, dx, dy, abs(dnx), abs(dny)); /* Discard the temporary mapping. */ free_mapping (w, &p_mp); return (status); } /* GtSetMapping -- Define a new mapping function, or modify an old one. * If a new mapping is defined it is merely enabled, and no refreshing * of the screen takes place until either some mapped data is written * to or the mapping is explicitly refreshed. If an existing mapping is * modified the old and new mappings are examined and only those portions * of the destination rect for which the mapping changed are updated. * This permits minor changes to a mapping (e.g. moving an edge) without * having to redraw the entire region. Regions of the destination drawable * which were previously covered by the mapping but which were exposed by * modifying the mapping are redrawn. */ GtSetMapping (w, mapping, rop, src,st,sx,sy,snx,sny, dst,dt,dx,dy,dnx,dny) GtermWidget w; int mapping; /* mapping number */ int rop; /* rasterop */ int src; /* 0=window, >0 = raster number */ int st; /* coordinate type for source raster */ int sx,sy,snx,sny; /* source raster */ int dst; /* 0=window, >0 = raster number */ int dt; /* coordinate type for source raster */ int dx,dy,dnx,dny; /* destination raster */ { register int i, j; register Mapping mp, o_mp, n_mp; struct mapping pix_mp, new_mp; int defined, scale_changed, offset, current, state, old_i; int nx, xs[MAX_REGIONS], xe[MAX_REGIONS], xv[MAX_REGIONS]; int ny, ys[MAX_REGIONS], ye[MAX_REGIONS], yv[MAX_REGIONS]; int n_dnx, n_dny, n_xflip=0, n_yflip=0, i1, i2; int o_dnx, o_dny, o_xflip=0, o_yflip=0; int *o_xymap, *o_xmap, *o_ymap; int *n_xymap, *n_xmap, *n_ymap; XRectangle rl[MAX_REGIONS]; int nrect, buflen, refresh; /* Check mapping number in range. */ if (mapping < 0 || mapping >= w->gterm.maxMappings) return (ERR); else mp = &w->gterm.mappings[mapping]; invalidate_draw_context (w); initialize_mapping (&pix_mp); initialize_mapping (&new_mp); /* Get local pixel space copy of old mapping, store new mapping. */ get_pixel_mapping (w, mp, &pix_mp, 1); mp->src = src; mp->st = st; mp->sx = sx; mp->sy = sy; mp->snx = snx; mp->sny = sny; mp->dst = dst; mp->dt = dt; mp->dx = dx; mp->dy = dy; mp->dnx = dnx; mp->dny = dny; mp->rop = (rop & ~(R_RefreshNone|R_RefreshAll)); mp->updated = 0; /* Newly defined mappings are linked at the tail of the mapping list, * i.e. they stack (display) on top of any other mappings. If the * mapping is already defined the stacking order is not changed. */ if (!(defined = mp->defined)) { mp_linkafter (w, mp, w->gterm.mp_tail); mp->defined = 1; } if (!valid_mapping (w, mp)) { mp_unlink (w, mp); mp->defined = 0; return (ERR); } update_mapping (w, mp); /* If we are defining a new mapping just define it and quit, without * refreshing the window, unless R_RefreshAll is explicitly set in the * mapping. If the mapping is not enabled merely store the new mapping. * If the mapping is a null mapping (no pixels) do nothing. If refresh * is disabled in the rasterop merely store the new mapping. If we are * editing an existing mapping which is enabled with the default rasterop, * we continue on to compare the old and new mappings and refresh any * changed pixels in the destination rect. */ if (!defined || src != mp->src || dst != mp->dst) { mp->enabled = mp->defined = 1; mp->refresh = 0; return; } else if (!mp->enabled) { return; } else if (snx == 0 || sny == 0 || dnx == 0 || dny == 0) return; if (rop & R_RefreshNone) return; /* Convert input mappings to pixel coordinates, we deal with only pixel * coordinates from here on. */ get_pixel_mapping (w, mp, &new_mp, 1); load_mapping (&new_mp, &mapping, &rop, &src,&st,&sx,&sy,&snx,&sny, &dst,&dt,&dx,&dy,&dnx,&dny); update_mapping (w, n_mp = &new_mp); update_mapping (w, o_mp = &pix_mp); /* * We are editing an existing mapping. Determine what has changed in * the mapping and refresh the changed regions. */ /* Get old XY scaling maps. */ o_xmap = o_mp->x_srcpix; o_ymap = o_mp->y_srcpix; if ((o_dnx = o_mp->dnx) < 0) { o_dnx = -o_dnx; o_xflip = 1; } if ((o_dny = o_mp->dny) < 0) { o_dny = -o_dny; o_yflip = 1; } /* Get new XY scaling maps. */ n_xmap = n_mp->x_srcpix; n_ymap = n_mp->y_srcpix; if (dnx < 0) { dnx = -dnx; n_xflip = 1; } if (dny < 0) { dny = -dny; n_yflip = 1; } /* Refresh the entire region if the refresh flag is set, a flip has * occurred, or we are doing a complex dezoom mapping. */ refresh = (o_mp->refresh || (rop & R_RefreshAll)); if (n_xflip != o_xflip || n_yflip != o_yflip) refresh = 1; if (n_mp->scaling == M_DEZOOM) refresh = 1; /* Has the spatial scale changed? */ scale_changed = abs (o_mp->xscale - n_mp->xscale) > 1.0E-4 || abs (o_mp->yscale - n_mp->yscale) > 1.0E-4; /* If any of the above conditions are true refresh the entire mapping, * otherwise compare the old and new mappings and refresh any subregions * which have changed. */ if (refresh || scale_changed || n_mp->scaling == M_DEZOOM) { refresh_destination (w, n_mp, dx, dy, dnx, dny); } else { /* Compare the old and new mappings to see what needs to be * refreshed. For speed reasons we only want to refresh the pixels * which have been remapped. Any destination pixel in the new mapping * which does not map to the same source pixel as in the old mapping * must be refreshed. We examine each X and Y coordinate in the new * destination rect and see if the source coordinate this maps to is * the same as in the old mapping. If a given destination pixel [i,j] * maps to the same pixel [i,j] in the source as it did in the * previous mapping, we do not need to refresh that pixel. We examine * the X and Y axis in turn and build up a list of regions which do or * do not need to be refreshed. */ /* Get a list of ranges {XS,XE,XV} in X. */ nx = get_regions (xs,xe,xv, MAX_REGIONS, dx, dnx, n_xmap, o_mp->dx, o_dnx, o_xmap); /* Get a list of ranges {YS,YE,YV} in Y. */ ny = get_regions (ys,ye,yv, MAX_REGIONS, dy, dny, n_ymap, o_mp->dy, o_dny, o_ymap); /* The list of ranges in X and Y together define a raster of arbitary * sized subrectangles filling the destination rect. If the state * value is nonzero (bit 1 set) in either X or Y, the subrectangle * must be refreshed. The get_rects routine returns a list of the * rectangular subregions matching the given condition (bit 1 set in * either axis). Adjacent rectangles are merged to minimize the * number of calls to refresh_destination. */ nrect = get_rects (rl, MAX_REGIONS, xs,xe,xv,nx, ys,ye,yv,ny, 1,1); for (i=0; i < nrect; i++) refresh_destination (w, n_mp, rl[i].x, rl[i].y, rl[i].width, rl[i].height); } /* Refresh any lower level mappings exposed when the current mapping was * modified. These will be regions of the old rect not present in the * new, modified rect for the current mapping. */ nx = get_regions (xs,xe,xv, MAX_REGIONS, o_mp->dx, o_dnx, o_xmap, dx, dnx, n_xmap); ny = get_regions (ys,ye,yv, MAX_REGIONS, o_mp->dy, o_dny, o_ymap, dy, dny, n_ymap); nrect = get_rects (rl, MAX_REGIONS, xs,xe,xv,nx, ys,ye,yv,ny, 2,2); for (i=0; i < nrect; i++) { XRectangle r, in; Mapping mp; /* Clear the uncovered region. */ GtSetPixels (w, dst, GtPixel, rl[i].x, rl[i].y, rl[i].width, rl[i].height, GtGetClientPixel(w,0), 0); /* Refresh any lower level mappings the destination rects of * which intersect the uncovered region. */ for (mp = w->gterm.mp_head; mp && mp->mapping != mapping; mp = mp->next) { if (mp->enabled && mp->dst == dst) { r.x = mp->dx; r.y = mp->dy; r.width = mp->dnx; r.height = mp->dny; if (rect_intersect (&in, &r, &rl[i])) refresh_destination (w, mp, in.x, in.y, in.width, in.height); } } } free_mapping (w, n_mp); free_mapping (w, o_mp); mp = &w->gterm.mappings[mapping]; mp->refresh = 0; } /* GtGetMapping -- Return the external parameters of a mapping. If the * numberd mapping is undefined -1 is returned; 0 is returned if the * mapping is defined but not enabled, and 1 is returned if the mapping * is active. */ GtGetMapping (w, mapping, rop, src,st,sx,sy,snx,sny, dst,dt,dx,dy,dnx,dny) GtermWidget w; int mapping; /* mapping number */ int *rop; /* rasterop */ int *src; /* 0=window, >0 = raster number */ int *st; /* coordinate type for source raster */ int *sx,*sy,*snx,*sny; /* source raster */ int *dst; /* 0=window, >0 = raster number */ int *dt; /* coordinate type for source raster */ int *dx,*dy,*dnx,*dny; /* destination raster */ { register Mapping mp; if (mapping < 0 || mapping >= w->gterm.maxMappings) return (-1); else if (!(mp = &w->gterm.mappings[mapping])->defined) return (-1); *rop = mp->rop; *src = mp->src; *st = mp->st; *sx = mp->sx; *sy = mp->sy; *snx = mp->snx; *sny = mp->sny; *dst = mp->dst; *dt = mp->dt; *dx = mp->dx; *dy = mp->dy; *dnx = mp->dnx; *dny = mp->dny; return (mp->enabled != 0); } /* GtActiveMapping -- Query whether a mapping is active. */ GtActiveMapping (w, mapping) register GtermWidget w; int mapping; /* mapping number */ { register Mapping mp; if (mapping < 0 || mapping >= w->gterm.maxMappings) return (0); else if (!(mp = &w->gterm.mappings[mapping])->defined) return (0); return (mp->enabled != 0); } /* GtEnableMapping -- Enable a mapping. Enabling a mapping does not * update the destination unless the refresh flag is set. Enabling a * mapping activates the mapping so that any changes to the source will * be mapped to the destination. */ GtEnableMapping (w, mapping, refresh) GtermWidget w; int mapping; /* mapping number */ int refresh; /* refresh destination */ { register Mapping mp; invalidate_draw_context (w); if (mapping >= 0 && mapping < w->gterm.maxMappings) { mp = &w->gterm.mappings[mapping]; if (mp->defined) { if (!mp->enabled) { mp->enabled = True; if (refresh) GtRefreshMapping (w, mapping); } return (OK); } } return (ERR); } /* GtDisableMapping -- Disable a mapping. Disabling a mapping does not * affect the mapping definition, hence a disabled mapping may later be * reenabled. If the ERASE flag is set the destination region is redrawn * with the mapping disabled. */ GtDisableMapping (w, mapping, erase) GtermWidget w; int mapping; /* mapping number */ int erase; /* erase the destination */ { register int i; register Mapping mp, dmp; XRectangle r, d, in; invalidate_draw_context (w); if (mapping >= 0 && mapping < w->gterm.maxMappings) { mp = &w->gterm.mappings[mapping]; if (mp->defined) { if (mp->enabled) { mp->enabled = False; if (erase) { d.x = mp->dx; d.y = mp->dy; d.width = abs(mp->dnx); d.height = abs(mp->dny); /* Clear the uncovered region. */ GtSetPixels (w, mp->dst, GtPixel, d.x, d.y, d.width, d.height, GtGetClientPixel(w,0), 0); /* Refresh any lower level mappings the destination rects of * which intersect the uncovered region. */ for (dmp = w->gterm.mp_head; dmp && dmp->mapping != mapping; dmp = dmp->next) { if (dmp->enabled && dmp->dst == mp->dst) { r.x = dmp->dx; r.y = dmp->dy; r.width = dmp->dnx; r.height = dmp->dny; if (rect_intersect (&in, &r, &d)) refresh_destination (w, dmp, in.x, in.y, in.width, in.height); } } } } return (OK); } } return (ERR); } /* GtRefreshMapping -- Refresh the destination region defined by a mapping. */ GtRefreshMapping (w, mapping) GtermWidget w; int mapping; /* mapping number */ { register Mapping mp; struct mapping p_mp; if (mapping >= 0 && mapping < w->gterm.maxMappings) { mp = &w->gterm.mappings[mapping]; if (mp->defined) { if (mp->st != GtPixel || mp->dt != GtPixel) { initialize_mapping (&p_mp); get_pixel_mapping (w, mp, &p_mp, 1); update_mapping (w, mp = &p_mp); } else update_mapping (w, mp); refresh_destination (w, mp, mp->dx, mp->dy, abs(mp->dnx), abs(mp->dny)); if (mp == &p_mp) free_mapping (w, mp); } } } /* GtMapVector -- Map a point vector from the coordinate system of one raster * to another as defined by the given mapping. The mapping may be either in * the forward direction (dir = GtMap) or the reverse (dir = GtUnmap). The * point vector is maintained in floating point for this operation to avoid * loss of precision. The input and output vectors may be the same vector. */ GtMapVector (w, mapping, dir, pv1, pv2, npts) GtermWidget w; int mapping; int dir; /* GtMap, GtUnmap */ DPoint *pv1; DPoint *pv2; int npts; { register DPoint *ip = pv1; register DPoint *op = pv2; register Mapping mp; register int n; struct mapping p_mp; double xscale, xoffset; double yscale, yoffset; int sx, sy; xscale = yscale = 1.0; xoffset = yoffset = 0.0; sx = sy = 0; if (mapping >= 0 && mapping < w->gterm.maxMappings) { mp = &w->gterm.mappings[mapping]; if (valid_mapping (w, mp)) { /* Determine the transformation coefficients. */ get_pixel_mapping (w, mp, &p_mp, 0); mp = &p_mp; xscale = (float)mp->dnx / (float)mp->snx; if (xscale < 0) xoffset = mp->dx + abs(mp->dnx) - 1; else xoffset = mp->dx; yscale = (float)mp->dny / (float)mp->sny; if (yscale < 0) yoffset = mp->dy + abs(mp->dny) - 1; else yoffset = mp->dy; sx = mp->sx; sy = mp->sy; } } /* Map the vector. */ if (dir == GtMap) { for (n=npts; --n >= 0; ip++, op++) { op->x = (ip->x - sx) * xscale + xoffset; op->y = (ip->y - sy) * yscale + yoffset; } } else { for (n=npts; --n >= 0; ip++, op++) { op->x = (ip->x - xoffset) / xscale + sx; op->y = (ip->y - yoffset) / yscale + sy; } } } /* GtPixelToNDC -- Transform a vector from pixel to NDC coordinates in the * coordinate system of the given reference raster. The input and output * vectors may be the same vector. */ GtPixelToNDC (w, raster, pv1, pv2, npts) GtermWidget w; int raster; DPoint *pv1; DPoint *pv2; int npts; { register Raster rp = &w->gterm.rasters[raster]; register DPoint *ip = pv1; register DPoint *op = pv2; register int n; for (n=npts; --n >= 0; ip++, op++) { op->x = ( ip->x) / rp->width * MAXNDC; op->y = (rp->height - ip->y) / rp->height * MAXNDC; } } /* GtNDCToPixel -- Transform a vector from NDC to pixel coordinates in the * coordinate system of the given reference raster. The input and output * vectors may be the same vector. */ GtNDCToPixel (w, raster, pv1, pv2, npts) GtermWidget w; int raster; DPoint *pv1; DPoint *pv2; int npts; { register Raster rp = &w->gterm.rasters[raster]; register DPoint *ip = pv1; register DPoint *op = pv2; register int n; for (n=npts; --n >= 0; ip++, op++) { op->x = ip->x / MAXNDC * rp->width; op->y = rp->height - (ip->y / MAXNDC * rp->height); } } /* GtDebug -- Print debug info. If the file descriptor is NULL output is * to the process stdout. The "what" argument may be used to select the * type of output desired. If what=0 the full output is generated, * otherwise bits are used to select what output is to be generated. * * "what" bitflags: * * 001 Widget information * 002 List rasters * 004 List mappings * 010 List colormaps * 020 List markers * * This routine is intended only for use during debugging. */ GtDebug (w, fp, what) GtermWidget w; FILE *fp; int what; { /* Default is to write everything to the stdout. */ what = what ? what : 0777; fp = fp ? fp : stdout; /* Print widget header. */ if (what & 001) { fprintf (fp, "Widget 0x%x (%s) %dx%d raster=%d\n", w, w->core.name, w->core.width, w->core.height, w->gterm.raster); fprintf (fp, "--------------------------------------------------------------\n"); } /* Print raster information. */ if (what & 002) { register int i; register Raster rp; if (w->gterm.rasters) { for (i=0; i < w->gterm.maxRasters; i++) { rp = &w->gterm.rasters[i]; if (!rp->type) continue; fprintf (fp, "raster %4d type=%s delete=%d size=%dx%d\n", i, rp->type == ImageRaster ? "client" : "server", rp->delete, rp->width, rp->height); } } else fprintf (fp, "no rasters\n"); } /* Print mapping information. */ if (what & 004) { register int i; register Mapping mp; char flags[32]; if (w->gterm.mappings) { for (i=0; i < w->gterm.maxMappings; i++) { mp = &w->gterm.mappings[i]; if (!mp->defined) continue; flags[0] = mp->enabled ? 'E' : 'D'; flags[1] = mp->updated ? 'U' : ' '; flags[2] = mp->refresh ? 'R' : ' '; flags[3] = '\0'; fprintf (fp, "mapping %3d %s %8o", i, flags, mp->rop); fprintf (fp, " %2d %s %3d %3d %3d %3d", mp->src, mp->st == GtPixel ? "pix" : "ndc", mp->sx, mp->sy, mp->snx, mp->sny); fprintf (fp, " %2d %s %3d %3d %3d %3d\n", mp->dst, mp->dt == GtPixel ? "pix" : "ndc", mp->dx, mp->dy, mp->dnx, mp->dny); } } else fprintf (fp, "no mappings\n"); fprintf (fp, "mappings from head: "); for (mp = w->gterm.mp_head; mp; mp = mp->next) fprintf (fp, " %d", mp->mapping); fprintf (fp, "\n"); fprintf (fp, "mappings from tail: "); for (mp = w->gterm.mp_tail; mp; mp = mp->prev) fprintf (fp, " %d", mp->mapping); fprintf (fp, "\n"); } /* Print colormap information. */ if (what & 010) { register struct colormap *cm; fprintf (fp, "cmapName=%s ncolors=%d basePixel=%d\n", w->gterm.cmapName, w->gterm.ncolors, w->gterm.base_pixel); for (cm = w->gterm.colormaps; cm; cm = cm->next) fprintf (fp, "colormap %2d ncells=%d\n", cm->map, cm->ncells); } /* Print marker information. */ if (what & 020) { register Marker mm; char value[256]; for (mm = w->gterm.gm_head; mm; mm = mm->next) { GmGetAttribute (mm, GmType, (XtArgVal)value, XtRString); fprintf (fp, "marker 0x%x: %10s flags=0x%x [%d %d %d %d] %0.5g\n", mm, value, mm->flags, mm->x, mm->y, mm->width, mm->height, mm->rotangle); } } } /* * Internal procedures for the above code. * ---------------------------------------- */ /* get_colormap -- Get a private colormap. On all calls after the first * this just returns the existing gterm widget colormap. On the first call * we query the server for the named custom colormap, and if the colormap * exists we modify the gterm widget to use it. If the custom colormap has * not yet been created by some other client, we create it. * * This code creates a custom colormap using the "standard colormap" * facilities provided by XLIB. Although we do not use any of the predefined * standard colormaps, use of the standard colormap facilities allows any * number of clients to share the same custom colormap. Use of a custom * colormap helps avoid running out of space in the default colormap, ensures * that the gterm widget will get the color cells it needs, and makes it * easier for several imaging clients which share the same colormap to * simultaneously display their windows. * * To minimize colormap flashing we try to avoid using the full colormap, * setting the unused cells to the colors set in the default colormap. In * most cases this will prevent the rest of the screen from changing color * when the custom colormap is installed. */ static Colormap get_colormap (w) GtermWidget w; { register int i, j; Display *display = w->gterm.display; Screen *screen = w->gterm.screen; XColor def_colors[SZ_STATIC_CMAP], *cp, *c1, *c2; XStandardColormap cm, *cm_p; XColor colors[MAX_SZCMAP]; int base_pixel, p1, p2; Colormap colormap; char property[128]; int ncmap, nitems; Pixel pixel; Atom atom; if (w->gterm.haveColormap) return (w->core.colormap); /* Map custom colormap name to atom. */ sprintf (property, "GT_%s", w->gterm.cmapName); atom = XInternAtom (display, property, False); w->gterm.cmapAtom = atom; /* Get custom colormap. */ if (!w->gterm.cmapInitialize && XGetRGBColormaps (display, w->gterm.root, &cm_p, &ncmap, atom)) { /* Colormap aleady exists, just use it. */ cm = *cm_p; colormap = cm.colormap; w->gterm.base_pixel = cm.base_pixel; } else { /* Create or reinitialize a global colormap. */ XVisualInfo template, *vi; Display *d; Screen *s; Window root; long mask; if (!(d = XOpenDisplay (DisplayString(display)))) goto use_default; s = DefaultScreenOfDisplay (d); root = DefaultRootWindow (d); /* Try to get a pseudocolor visual. */ mask = 0; template.screen = DefaultScreen(d); mask |= VisualScreenMask; template.depth = RasterDepth; mask |= VisualDepthMask; template.class = PseudoColor; mask |= VisualClassMask; if (!(vi = XGetVisualInfo (d, mask, &template, &nitems))) { XCloseDisplay (d); goto use_default; } /* Create custom colormap with all cells allocated read/write */ colormap = XCreateColormap (d, root, vi->visual, AllocAll); /* Initialize colormap to be same as default colormap. */ nitems = min (MAX_SZCMAP, CellsOfScreen(s)); for (i=0; i < nitems; i++) colors[i].pixel = i; XQueryColors (d, DefaultColormapOfScreen(s), colors, nitems); XStoreColors (d, colormap, colors, nitems); /* Globally define permanent server custom colormap. */ memset ((char *)&cm, 0, sizeof(cm)); cm.colormap = colormap; cm.base_pixel = w->gterm.base_pixel; cm.red_max = 0; cm.visualid = vi->visualid; cm.killid = 1; XSetRGBColormaps (d, root, &cm, 1, atom); XSetCloseDownMode (d, RetainPermanent); XCloseDisplay (d); w->gterm.cmapInitialize = False; } /* Save default color assignments for static colors. */ for (i=0; i < SZ_STATIC_CMAP; i++) def_colors[i] = w->gterm.color[i]; nitems = min (MAX_SZCMAP, CellsOfScreen(screen)); w->gterm.ncolors = SZ_STATIC_CMAP + w->gterm.maxColors; base_pixel = w->gterm.base_pixel; /* Get the private colormap. */ for (i=0; i < nitems; i++) colors[i].pixel = i; XQueryColors (display, colormap, colors, nitems); /* Initialize the raster pixel to display pixel mapping and set the * color assigned to each pixel value in the private colormap. */ for (i = SZ_STATIC_CMAP; i < w->gterm.ncolors; i++) { w->gterm.color[i].pixel = w->gterm.cmap[i] = pixel = min (nitems - 1, base_pixel + i - SZ_STATIC_CMAP); w->gterm.color[i] = colors[pixel]; } /* Check the static part of the cmap to make sure that the pixel numbers * aren't aliased to pixels in the dynamic part of the custom colormap. * If this happens, reassign these color numbers to the pixels just * preceeding the dynamic part of the custom colormap. The red_max * field of the colormap descriptor is used to keep track of the number * of static colors allocated by different clients. These static colors * are shared, hence the same color will not be stored twice. */ p1 = p2 = base_pixel - cm.red_max; for (i=0; i < SZ_STATIC_CMAP; i++) { pixel = w->gterm.cmap[i]; if (pixel >= base_pixel && pixel < base_pixel+DEF_MAXCOLORS && p1 > 2) { /* First check to see if we already have a static entry reserved * for this color. */ c1 = &def_colors[i]; for (j=p1, cp=NULL; j < base_pixel; j++) { c2 = &colors[j]; if (c1->red == c2->red && c1->green == c2->green && c1->blue == c2->blue) { cp = c2; break; } } /* Assign a new screen pixel value. */ if (cp) w->gterm.cmap[i] = cp->pixel; else { cp = &colors[--p1]; *cp = def_colors[i]; cp->flags = (DoRed | DoGreen | DoBlue); cp->pixel = w->gterm.cmap[i] = p1; cm.red_max++; } w->gterm.color[i].pixel = w->gterm.cmap[i]; } } if (p1 < p2) { XStoreColors (display, colormap, &colors[p1], p2 - p1); XSetRGBColormaps (display, w->gterm.root, &cm, 1, atom); } /* Assign the new colormap to the gterm widget's window. */ XtVaSetValues ((Widget)w, XtNcolormap, (XtArgVal)colormap, NULL); w->gterm.haveColormap++; /* If the pointer is in the window, advise window manager to load the * colortable for the window. */ if (w->gterm.in_window) request_colormap_focus (w); return (colormap); use_default: /* Unable to create custom colormap. */ w->gterm.useDefaultCM++; w->gterm.haveColormap++; return (w->core.colormap); } /* request_colormap_focus -- Modify the WM_COLORMAP_WINDOWS property on a * widget's top level shell window to advise the window manager that the * widget's window should have its colormap loaded. This should only be * used for windows that have a colormap different than that of the top * level window. */ static request_colormap_focus (w) GtermWidget w; { Widget p; if (!XtIsRealized ((Widget)w)) return; /* Find the top level window. */ for (p = XtParent(w); !XtIsShell(p); p = XtParent(p)) ; /* Modify WM_COLORMAP_WINDOWS to give the current window priority. */ if (p) { Window window = XtWindow (p); Window *wl = NULL, n_wl[MAX_WMWIN+1]; register int n_nw, i; int nw; /* If WM_COLORMAP_WINDOWS is already set save its value, otherwise * start a list initially containing only the top level window. */ w->gterm.wmTop = window; if (XGetWMColormapWindows (w->gterm.display, window, &wl, &nw)) { memmove (w->gterm.wmWindows, (char *)wl, nw * sizeof(int)); w->gterm.n_wmWindows = nw = min (nw, MAX_WMWIN); free ((char *)wl); } else { w->gterm.wmWindows[0] = window; w->gterm.n_wmWindows = nw = 1; } n_nw = 0; wl = w->gterm.wmWindows; n_wl[n_nw++] = XtWindow(w); for (i=0; i < nw; i++) if (wl[i] != XtWindow(w)) n_wl[n_nw++] = wl[i]; XSetWMColormapWindows (w->gterm.display, window, n_wl, n_nw); } } /* restore_colormap_focus -- Reset WM_COLORMAP_WINDOWS. Retain the window * that had the focus in the list, but drop its priority one notch. This * should follow a prior call to request_colormap_focus. */ static restore_colormap_focus (w) GtermWidget w; { register int nw, n_nw, i; Window *wl, n_wl[MAX_WMWIN+1], old; if (!XtIsRealized ((Widget)w)) return; old = XtWindow(w); wl = w->gterm.wmWindows; if ((nw = w->gterm.n_wmWindows) == 0 || (nw == 1 && wl[0] == old)) return; n_nw = 0; if (wl[0] != old) n_wl[n_nw++] = wl[0]; n_wl[n_nw++] = old; for (i=1; i < nw; i++) if (wl[i] != old) n_wl[n_nw++] = wl[i]; XSetWMColormapWindows (w->gterm.display, w->gterm.wmTop, n_wl, n_nw); } /* inherit_default_colormap -- Set any unused cells of the custom colormap * to the colors defined for the corresponding cells of the default colormap. * This minimizes colormap flashing when using a custom colormap, but only * works if a few unused cells can be reserved, e.g., at the beginning of * the colormap (which is usually where X allocates its colors). */ static inherit_default_colormap (w) GtermWidget w; { register XColor *cp, *ap; register int ncolors, i; Display *display = w->gterm.display; Screen *screen = w->gterm.screen; Window root = w->gterm.root; Atom atom = w->gterm.cmapAtom; XColor colors[MAX_SZCMAP]; XStandardColormap *cm; int first, nitems, ncmap; if (!XtIsRealized ((Widget)w)) return; if (w->gterm.useDefaultCM || !w->gterm.haveColormap) return; if (w->gterm.base_pixel <= 0) return; /* fully allocated colormap */ /* We have to read the colormap property again as another client could * have reserved more static colors (i.e.,changed red_max), and we don't * want to clobber these colors. */ if (XGetRGBColormaps (display, root, &cm, &ncmap, atom)) { /* Make sure we have the right colormap. */ if (w->core.colormap != cm->colormap) XtVaSetValues ((Widget)w,XtNcolormap,(XtArgVal)cm->colormap,NULL); /* Get lower part of default colormap. */ ncolors = cm->base_pixel - cm->red_max; for (cp=colors, i=0; i < ncolors; i++, cp++) { cp->flags = (DoRed | DoGreen | DoBlue); cp->pixel = i; } /* Get upper part of default colormap. */ first = cm->base_pixel + w->gterm.ncolors - SZ_STATIC_CMAP; ncolors = min (MAX_SZCMAP, CellsOfScreen(screen)) - first; for (i=0; i < ncolors; i++, cp++) { cp->flags = (DoRed | DoGreen | DoBlue); cp->pixel = first + i; } /* Inherit values from default colormap. */ ncolors = cp - colors; XQueryColors (display, DefaultColormapOfScreen(screen), colors, ncolors); XStoreColors (display, w->core.colormap, colors, ncolors); /* The global gterm colormap may have changed. Compare widget's * version of color table with the global colortable and update * the widget's state if the global colortable has changed. */ ncolors = w->gterm.ncolors; memmove (colors, w->gterm.color, ncolors * sizeof(*cp)); XQueryColors (display, w->core.colormap, colors, ncolors); for (i=ncolors, cp=colors, ap=w->gterm.color; --i >= 0; cp++, ap++) if (cp->red != ap->red || cp->green != ap->green || cp->blue != ap->blue) { memmove (w->gterm.color, colors, ncolors * sizeof(*cp)); invalidate_cmap (w); } } } /* update_default_colormap -- Update the default colormap so that any * unallocated cells mirror the widget's custom colormap. This increases * the chance that the widget's contents will be visible when the window * does not have the colormap focus, and minimizes flashing when the * colormap focus changes. */ static update_default_colormap (w) GtermWidget w; { register XColor *ip, *op; register int j, n; register Pixel v; XColor colors[MAX_SZCMAP]; Pixel pixels[MAX_SZCMAP]; char allocated[MAX_SZCMAP]; int overflow, req, need, first, nelem, i; unsigned long plane_masks[1]; Colormap defcmap; if (!XtIsRealized ((Widget)w)) return; if (w->gterm.useDefaultCM || !w->gterm.haveColormap) return; first = SZ_STATIC_CMAP; nelem = w->gterm.ncolors; defcmap = DefaultColormapOfScreen (w->gterm.screen); /* need = min (MAX_SZCMAP, first + nelem - SZ_STATIC_CMAP); */ need = MAX_SZCMAP; /* Get the colormap cells. */ for (req=need, n=0; req > 0 && n < need; ) if (XAllocColorCells (w->gterm.display, defcmap, False, plane_masks, 0, &pixels[n], req)) { n += req; } else req /= 2; /* Perform the color matching. This is awkward as the pixel value * assignments may be different in the two colormaps. We have to look * up each pixel before attempting to assign a color, or XStoreColors * below will result in a server error. */ memset (allocated, 0, sizeof(allocated)); overflow = 0; for (i=0; i < n; i++) { v = pixels[i]; if (v < MAX_SZCMAP) allocated[v] = 1; else { overflow++; break; } } ip = &w->gterm.color[first]; op = colors; if (overflow) { for (i=0; i < nelem; i++, ip++) for (j=0, v = ip->pixel; j < n; j++) if (pixels[j] == v) { *op++ = *ip; break; } } else { for (j=0; j < nelem; j++, ip++) if (allocated[ip->pixel]) { allocated[ip->pixel] = 0; *op++ = *ip; } } if (op > colors) XStoreColors (w->gterm.display, defcmap, colors, op - colors); XFreeColors (w->gterm.display, defcmap, pixels, n, 0); } /* refresh_source -- Refresh a portion of a mapping given the source rect * to be refreshed. If the given source rect is not within the mapping, * this is a no-op. */ static refresh_source (w, mp, x1, y1, nx, ny) GtermWidget w; register Mapping mp; /* mapping defining refresh operation */ int x1, y1, nx, ny; /* region of source to be refreshed */ { int sx1, sx2, sy1, sy2, snx, sny; int dx1, dx2, dy1, dy2, dnx, dny; /* Do nothing if mapping not defined and enabled. */ if (!valid_mapping (w, mp)) return (ERR); if (!mp->enabled) return (OK); /* Compute the intersection of the modified region of the source raster * with the rectangular region of the source raster affected by the given * mapping. */ sx1 = max (x1, mp->sx); sy1 = max (y1, mp->sy); sx2 = min(x1 + nx, mp->sx + mp->snx) - 1; sy2 = min(y1 + ny, mp->sy + mp->sny) - 1; snx = sx2 - sx1 + 1; sny = sy2 - sy1 + 1; /* Do nothing if the rectangles do not intersect. */ if (snx <= 0 || sny <= 0) return (OK); /* Compute the destination rect affected by the mapped source rect. */ dx1 = mp->x_extent[sx1 - mp->sx].lo; dx2 = mp->x_extent[sx2 - mp->sx].hi; if (dx1 > dx2) { dx1 = mp->x_extent[sx2 - mp->sx].lo; dx2 = mp->x_extent[sx1 - mp->sx].hi; } dy1 = mp->y_extent[sy1 - mp->sy].lo; dy2 = mp->y_extent[sy2 - mp->sy].hi; if (dy1 > dy2) { dy1 = mp->y_extent[sy2 - mp->sy].lo; dy2 = mp->y_extent[sy1 - mp->sy].hi; } dnx = dx2 - dx1 + 1; dny = dy2 - dy1 + 1; /* Perform the refresh operation. */ return (refresh_destination (w, mp, dx1, dy1, dnx, dny)); } /* refresh_destination -- Refresh (redraw) the pixels in the given destination * rect. The mapping operand defines how to generate the value of each output * pixel. This is the routine which does all the real work of a mapping, * computing the values of the output pixels and writing to the destination * drawable. Note: the destination rect must be specified in raster pixel * coordinates (no NDC). */ static refresh_destination (w, mp, x1, y1, nx, ny) GtermWidget w; Mapping mp; /* mapping defining refresh operation */ int x1, y1, nx, ny; /* region of destination to be refreshed */ { Raster sr, dr; Display *display = w->gterm.display; int scaling, xflip, yflip, delxin=0, delxout=0; int ox, oy, rop, clip, mapping, i, j; int src, st, sx, sy, snx, sny; int dst, dt, dx, dy, dnx, dny; int xoff, yoff, p1, p2, q1, q2; float xscale, yscale; struct mapping *np, p_mp; XImage *xin, *xout; int status = OK; Region clip_region, mask_region; uchar *old_xin_lp, *old_xout_lp; uchar *xin_lp, *xout_lp, *op; int xin_bpl, xout_bpl; int *xmap, *ymap; XRectangle r; if (!XtIsRealized ((Widget)w)) return (OK); /* Do nothing if mapping not defined and enabled. */ if (!valid_mapping (w, mp)) return (ERR); if (!mp->enabled) return (OK); /* Offsets into the x_*,y_* mapping lookup tables. */ xoff = x1 - mp->dx; yoff = y1 - mp->dy; /* Get source and destination rects. */ dst = mp->dst; dx = x1; dy = y1; dnx = nx; dny = ny; src = mp->src; p1 = mp->x_srcpix[xoff]; q1 = mp->y_srcpix[yoff]; p2 = mp->x_srcpix[xoff + nx - 1]; q2 = mp->y_srcpix[yoff + ny - 1]; sx = min (p1, p2); sy = min (q1, q2); snx = abs (p2 - p1) + 1; sny = abs (q2 - q1) + 1; /* Define some local variables. */ sr = &w->gterm.rasters[src]; dr = &w->gterm.rasters[dst]; mapping = mp->mapping; scaling = mp->scaling; xscale = mp->xscale; yscale = mp->yscale; rop = mp->rop; if (!sr->type || !dr->type) return (ERR); if (snx <= 0 || sny <= 0 || dnx == 0 || dny == 0) return (ERR); if (src < 0 || src > w->gterm.maxRasters || dst < 0 || dst > w->gterm.maxRasters) return (ERR); /* Do we have a flip in X or Y? */ xflip = mp->dnx < 0; yflip = mp->dny < 0; /* Any higher numbered mappings which map to the same destination as the * mapping MP will obscure the current mapping. Construct a clip mask * defining the region of the destination we can write to. This will be * the region not obscured by any higher numbered, active mappings. */ clip = False; clip_region = XCreateRegion(); r.x = dx; r.y = dy; r.width = dnx; r.height = dny; #ifdef sun /* As far as I can tell the Sun (probably in the OW X server) has an * off by one bug affecting clipping in the server. A clip region is * too small by one pixel at the right and bottom, causing these pixels * to not be written when refreshing the screen (usually this shows up * as black lines on the screen when a region is refreshed). So far * I haven't seen this on any other platform. The problem is imperfectly * understood and the following workaround may not completely workaround * the problem. */ r.width++; r.height++; #endif XUnionRectWithRegion (&r, clip_region, clip_region); for (np = mp->next; np; np = np->next) { struct mapping p_np; if (!np->enabled || np->dst != dst) continue; get_pixel_mapping (w, np, &p_np, 0); r.x = p_np.dx; r.y = p_np.dy; r.width = abs(p_np.dnx); r.height = abs(p_np.dny); if (XRectInRegion (clip_region, r.x, r.y, r.width, r.height) != RectangleOut) { mask_region = XCreateRegion(); XUnionRectWithRegion (&r, mask_region, mask_region); XSubtractRegion (clip_region, mask_region, clip_region); XDestroyRegion (mask_region); clip++; } } if (clip && dr->type == PixmapRaster) XSetRegion (w->gterm.display, w->gterm.exposeGC, clip_region); /* Handle the special case of a pixmap to pixmap (or window) copy in * the server with no scaling. */ if (!scaling && sr->type == PixmapRaster && dr->type == PixmapRaster) { if (src == 0 && dst == 0 && w->gterm.pixmap && !(rop & R_Transient)) { XCopyArea (display, w->gterm.pixmap, w->gterm.pixmap, w->gterm.exposeGC, sx, sy, snx, sny, dx, dy); XCopyArea (display, w->gterm.pixmap, dr->r.pixmap, w->gterm.exposeGC, dx, dy, dnx, dny, dx, dy); } else { XCopyArea (display, sr->r.pixmap, dr->r.pixmap, w->gterm.exposeGC, sx, sy, snx, sny, dx, dy); if (dst == 0 && w->gterm.pixmap && !(rop & R_Transient)) XCopyArea (display, sr->r.pixmap, w->gterm.pixmap, w->gterm.exposeGC, sx, sy, snx, sny, dx, dy); } goto done; } /* Any other case requires working on ximages in the client. The first * step is to get the source ximage. */ if (sr->type == PixmapRaster) { /* Source is a pixmap but we need a local copy as either the * destination is not a pixmap, or we need to do some scaling. */ xin = XGetImage (display, (src || !w->gterm.pixmap) ? sr->r.pixmap : w->gterm.pixmap, 0, 0, sr->width, sr->height, 0xff, ZPixmap); if (xin == NULL) { status = ERR; goto done; } delxin++; } else { /* Source is an ximage. */ xin = sr->r.ximage; } /* Handle the special case of a copy of an ximage to an output pixmap * with no scaling. */ if (!scaling && dr->type == PixmapRaster) { if (dst == 0 && w->gterm.pixmap && !(rop & R_Transient)) { XPutImage (display, w->gterm.pixmap, w->gterm.exposeGC, xin, sx, sy, dx, dy, dnx, dny); XCopyArea (display, w->gterm.pixmap, dr->r.pixmap, w->gterm.exposeGC, dx, dy, dnx, dny, dx, dy); } else { XPutImage (display, dr->r.pixmap, w->gterm.exposeGC, xin, sx, sy, dx, dy, dnx, dny); } goto done; } /* Get output ximage. */ if (dr->type == ImageRaster) { xout = dr->r.ximage; ox = dx; oy = dy; } else { uchar *data = (uchar *) XtMalloc (dnx * dny); if (data == NULL) { status = ERR; goto done; } xout = XCreateImage (w->gterm.display, NULL, RasterDepth, ZPixmap, 0, (char *)data, dnx, dny, 8, 0); if (xout == NULL) { XtFree ((char *)data); status = ERR; goto done; } ox = 0; oy = 0; delxout++; } xin_lp = (uchar *)xin->data; xout_lp = (uchar *)xout->data; xin_bpl = xin->bytes_per_line; xout_bpl = xout->bytes_per_line; /* Map a region of the input ximage XIN to the output ximage XOUT. Various * approaches are used to generate the output data, depending upon what * type of scaling we are doing. */ if (!scaling) { /* No scaling. Copy a region of the ximage xin to xout without * spatially scaling the image data. */ if (clip && dr->type == ImageRaster) goto zoom; xin_lp = (uchar *)xin->data + sy * xin_bpl; xout_lp = (uchar *)xout->data + oy * xout_bpl + ox; for (j=0; j < dny; j++) { memmove (xout_lp, xin_lp, dnx); xin_lp += xin_bpl; xout_lp += xout_bpl; } } else if (scaling == M_INTZOOM) { /* Integer zoom. The zoom factor is an integer, allowing the zoomed * image to be calculated without using the xmap,ymap lookup tables. */ if (clip && dr->type == ImageRaster) goto zoom; scale_intzoom (xin_lp,xin_bpl, xout_lp,xout_bpl, sx,sy, ox,oy,dnx,dny, xflip,yflip, (int)(xscale + 0.5), (int)(yscale + 0.5)); } else if (scaling == M_ZOOM) { /* We have a zoom, or one-to-many, scaling. Zoom scaling is always * done with pixel replication. The [xy]_srcpix arrays in the mapping * descriptor give the source pixel corresponding to each mapped pixel * in the destination raster. */ zoom: xmap = &mp->x_srcpix[xoff]; ymap = &mp->y_srcpix[yoff]; scale_zoom (xin_lp, xin_bpl, xout_lp, xout_bpl, xmap, ymap, ox, oy, dnx, dny, (clip && dr->type == ImageRaster) ? clip_region : (Region)NULL); } else if (scaling == M_DEZOOM) { /* We have a dezoom, or many-to-one, scaling. A block of pixels in * the input raster are combined to generate the value of each output * pixel, using one of several antialias algorithms to compute the * output pixel value. */ float *x_src = &mp->x_src[xoff]; float *y_src = &mp->y_src[yoff]; int near_unitary = (xscale > 0.5 && yscale > 0.5); int function; /* Get the antialising function to be applied. */ if (!(function = (mp->rop & R_MFMask))) function = MF_NEAREST; /* If the dezoom factor is small and either MF_BILINEAR or * MF_NEAREST is enabled, use the indicated method to sample the * input data. This uses all the data but minimizes smoothing. */ if ((function & (MF_BILINEAR|MF_NEAREST)) && near_unitary) function = (function & MF_BILINEAR) ? MF_BILINEAR : MF_NEAREST; else if (function != (function & (MF_BILINEAR|MF_NEAREST))) function &= ~(MF_BILINEAR|MF_NEAREST); filter: /* This can take a while so update the display. */ XFlush (w->gterm.display); /* If the filtering operation involves any arithmetic combinations * of pixels we must convert pixel numbers to pixel intensity values * before performing the filtering operation. This is a case where * we would be better off if frame buffers were maintained using * pixel intensities rather than hardware pixel numbers. */ if (function != MF_NEAREST) { uchar *data = (uchar *) XtMalloc (xin->width * xin->height); if (data == NULL) { status = ERR; goto done; } mf_getinten (w, xin_lp, xin->width, xin->height, xin_bpl, sx,sy, data, xin->width, xin->height, xin_bpl, sx,sy, snx,sny); if (!delxin) { xin = XCreateImage (w->gterm.display, NULL, RasterDepth, ZPixmap, 0, (char *)data, xin->width, xin->height, 8, 0); if (xin == NULL) { XtFree ((char *)data); status = ERR; goto done; } delxin++; } else { XtFree ((char *)xin->data); xin->data = (char *) data; } xin_lp = (uchar *)xin->data; } /* Filter the source rect to the destination. */ switch (function) { case MF_NEAREST: scale_nearest ( xin_lp, xin->width, xin->height, xin_bpl, xout_lp, xout->width, xout->height, xout_bpl, x_src, y_src, ox, oy, dnx, dny, (clip && dr->type == ImageRaster) ? clip_region : (Region)NULL ); break; case MF_BILINEAR: scale_bilinear ( xin_lp, xin->width, xin->height, xin_bpl, xout_lp, xout->width, xout->height, xout_bpl, x_src, y_src, ox, oy, dnx, dny, (clip && dr->type == ImageRaster) ? clip_region : (Region)NULL ); break; case MF_BLKAVG: scale_boxcar ( xin_lp, xin->width, xin->height, xin_bpl, xout_lp, xout->width, xout->height, xout_bpl, x_src,y_src, sx,sy,snx,sny, ox,oy,dnx,dny, xscale,yscale, 0, clip ? clip_region : (Region)NULL ); break; case MF_BOXCAR: scale_boxcar ( xin_lp, xin->width, xin->height, xin_bpl, xout_lp, xout->width, xout->height, xout_bpl, x_src,y_src, sx,sy,snx,sny, ox,oy,dnx,dny, xscale,yscale, 1, clip ? clip_region : (Region)NULL ); break; case MF_LOWPASS: scale_lowpass ( xin_lp, xin->width, xin->height, xin_bpl, xout_lp, xout->width, xout->height, xout_bpl, x_src,y_src, sx,sy,snx,sny, ox,oy,dnx,dny, xscale,yscale, clip ? clip_region : (Region)NULL ); break; default: function = MF_BILINEAR; goto filter; } /* If the operation was performed in intensity space convert back * to pixel number. */ if (function != MF_NEAREST) mf_getpixel (w, xout_lp, xout->width, xout->height, xout_bpl, ox,oy, xout_lp, xout->width, xout->height, xout_bpl, ox,oy, dnx,dny); } else { status = ERR; goto done; } /* Copy the scaled ximage to the output pixmap, if any. */ if (dr->type == PixmapRaster) { if (dst == 0 && w->gterm.pixmap && !(rop & R_Transient)) { XPutImage (display, w->gterm.pixmap, w->gterm.exposeGC, xout, ox, oy, dx, dy, dnx, dny); XCopyArea (display, w->gterm.pixmap, dr->r.pixmap, w->gterm.exposeGC, dx, dy, dnx, dny, dx, dy); } else { XPutImage (display, dr->r.pixmap, w->gterm.exposeGC, xout, ox, oy, dx, dy, dnx, dny); } } done: /* Clean up. */ if (delxin) XDestroyImage (xin); if (delxout) XDestroyImage (xout); XDestroyRegion (clip_region); if (clip && dr->type == PixmapRaster) XSetClipMask (w->gterm.display, w->gterm.exposeGC, None); /* Execute any mappings defined on the raster just updated. */ if (status == OK) { GtRefreshPixels (w, dst, GtPixel, dx, dy, dnx, dny); if (dst == 0) { Region clip_region = (Region)NULL; XRectangle r; clip_region = XCreateRegion(); r.x = dx; r.y = dy; r.width = dnx; r.height = dny; XUnionRectWithRegion (&r, clip_region, clip_region); update_transients (w, clip_region); XDestroyRegion (clip_region); } } return (status); } /* scale_zoom -- Compute the given destination rect from the input image, * using pixel replication and the given x and y dst->scr pixels maps. */ static scale_zoom (idata,ibpl, odata,obpl, xmap,ymap, dx,dy,dnx,dny, clip_region) uchar *idata, *odata; /* input, output data */ int ibpl, obpl; /* bytes per line */ register int *xmap; /* src coords of each dst pixel */ int *ymap; /* src coords of each dst pixel */ int dx, dy, dnx, dny; /* destination rect */ Region clip_region; /* clip Region or null */ { register int i, j; register uchar *ip, *op; uchar *last_ip = NULL; uchar *last_op = NULL; for (j=0; j < dny; j++) { ip = idata + ymap[j] * ibpl; op = odata + (j+dy) * obpl + dx; if (!clip_region) { if (ip == last_ip) memmove (op, last_op, dnx); else { for (i=0; i < dnx; i++) op[i] = ip[xmap[i]]; } last_ip = ip; last_op = op; } else { for (i=0; i < dnx; i++) if (XPointInRegion (clip_region, i + dx, j + dy)) op[i] = ip[xmap[i]]; } } } /* scale_intzoom -- Compute the given destination rect from the input image, * using pixel replication and integer scaling. This is functionally * equivalent to scale_zoom using the lookup tables, but optimized for the * case of integer scaling. */ static scale_intzoom (idata,ibpl,odata,obpl, sx,sy,dx,dy,dnx,dny, xflip,yflip, nx,ny) uchar *idata, *odata; /* input, output data */ int ibpl, obpl; /* bytes per line */ int sx, sy; /* start coords of src rect */ int dx, dy, dnx, dny; /* destination rect */ int xflip, yflip; /* set if x or y is flipped */ int nx, ny; /* replication factors */ { register int n; register int pix; register uchar *ip, *op; uchar *otop, *olast, *lp; int i, j, k; olast = odata + (dy + dny) * obpl - dnx + dx; if (xflip) { for (j=0, k=0; j < dny; j += ny, k++) { ip = idata + (sy + k) * ibpl + sx; op = odata + (dy + yflip ? (dny-ny-j) : j) * obpl + dx + dnx; otop = lp = op - dnx; /* Replicate a block of pixels. */ switch (nx) { case 2: for (n = (dnx/2); --n >= 0; ) { pix = *ip++; *--op = pix; *--op = pix; } break; case 3: for (n = (dnx/3); --n >= 0; ) { pix = *ip++; *--op = pix; *--op = pix; *--op = pix; } break; case 4: for (n = (dnx/4); --n >= 0; ) { pix = *ip++; *--op = pix; *--op = pix; *--op = pix; *--op = pix; } break; case 5: for (n = (dnx/5); --n >= 0; ) { pix = *ip++; *--op = pix; *--op = pix; *--op = pix; *--op = pix; *--op = pix; } break; case 6: for (n = (dnx/6); --n >= 0; ) { pix = *ip++; *--op = pix; *--op = pix; *--op = pix; *--op = pix; *--op = pix; *--op = pix; } break; case 7: for (n = (dnx/7); --n >= 0; ) { pix = *ip++; *--op = pix; *--op = pix; *--op = pix; *--op = pix; *--op = pix; *--op = pix; *--op = pix; } break; case 8: for (n = (dnx/8); --n >= 0; ) { pix = *ip++; *--op = pix; *--op = pix; *--op = pix; *--op = pix; *--op = pix; *--op = pix; *--op = pix; *--op = pix; } break; default: for (n = (dnx/nx); --n >= 0; ) { pix = *ip++; for (i=nx; --i >= 0; ) *--op = pix; } break; } /* Fill the last partial pixel. */ pix = *ip++; while (op > otop) *--op = pix; /* Replicate in Y. */ for (n=ny, op=lp; --n > 0; ) { op += obpl; if (op <= olast) memmove (op, lp, dnx); } } } else { for (j=0, k=0; j < dny; j += ny, k++) { ip = idata + (sy + k) * ibpl + sx; op = lp = odata + (dy + yflip ? (dny-ny-j) : j) * obpl + dx; otop = op + dnx; /* Replicate a block of pixels. */ switch (nx) { case 2: for (n = (dnx/2); --n >= 0; ) { pix = *ip++; *op++ = pix; *op++ = pix; } break; case 3: for (n = (dnx/3); --n >= 0; ) { pix = *ip++; *op++ = pix; *op++ = pix; *op++ = pix; } break; case 4: for (n = (dnx/4); --n >= 0; ) { pix = *ip++; *op++ = pix; *op++ = pix; *op++ = pix; *op++ = pix; } break; case 5: for (n = (dnx/5); --n >= 0; ) { pix = *ip++; *op++ = pix; *op++ = pix; *op++ = pix; *op++ = pix; *op++ = pix; } break; case 6: for (n = (dnx/6); --n >= 0; ) { pix = *ip++; *op++ = pix; *op++ = pix; *op++ = pix; *op++ = pix; *op++ = pix; *op++ = pix; } break; case 7: for (n = (dnx/7); --n >= 0; ) { pix = *ip++; *op++ = pix; *op++ = pix; *op++ = pix; *op++ = pix; *op++ = pix; *op++ = pix; *op++ = pix; } break; case 8: for (n = (dnx/8); --n >= 0; ) { pix = *ip++; *op++ = pix; *op++ = pix; *op++ = pix; *op++ = pix; *op++ = pix; *op++ = pix; *op++ = pix; *op++ = pix; } break; default: for (n = (dnx/nx); --n >= 0; ) { pix = *ip++; for (i=nx; --i >= 0; ) *op++ = pix; } break; } /* Fill the last partial pixel. */ pix = *ip++; while (op < otop) *op++ = pix; /* Replicate in Y. */ for (n=ny, op=lp; --n > 0; ) { op += obpl; if (op <= olast) memmove (op, lp, dnx); } } } } /* scale_nearest -- Compute the given destination rect from the input image, * using the nearest neighbor technique. */ static scale_nearest (idata,inx,iny,ibpl, odata,onx,ony,obpl, x_src,y_src, dx,dy,dnx,dny, clip_region) uchar *idata, *odata; /* input, output data */ int inx, iny, ibpl; /* dimensions of input array */ int onx, ony, obpl; /* dimensions of output array */ float *x_src, *y_src; /* src coords of each dst pixel */ int dx, dy, dnx, dny; /* destination rect */ Region clip_region; /* clip Region or null */ { register int m, n, i, j; register uchar *op; for (j=0; j < dny; j++) { op = odata + (j+dy) * obpl + dx; n = y_src[j]; if (!clip_region) { for (i=0; i < dnx; i++) { m = x_src[i]; op[i] = idata[n * ibpl + m]; } } else { for (i=0; i < dnx; i++) if (XPointInRegion (clip_region, i + dx, j + dy)) { m = x_src[i]; op[i] = idata[n * ibpl + m]; } } } } /* scale_bilinear -- Compute the given destination rect from the input image, * using bilinear interpolation. */ static scale_bilinear (idata,inx,iny,ibpl, odata,onx,ony,obpl, x_src,y_src, dx,dy,dnx,dny, clip_region) uchar *idata, *odata; /* input, output data */ int inx, iny, ibpl; /* dimensions of input array */ int onx, ony, obpl; /* dimensions of output array */ float *x_src, *y_src; /* src coords of each dst pixel */ int dx, dy, dnx, dny; /* destination rect */ Region clip_region; /* clip Region or null */ { register int i; register uchar *op; register float *lp, *w1, *w2; int buflen, line, *px, pixel, j; float lo_w, hi_w, x, y; uchar *lo, *hi; float *buf; buflen = (3 * dnx + 2) * sizeof(float) + dnx * sizeof(int); if ((buf = (float *) XtMalloc (buflen)) == NULL) return; lp = buf + 1; w1 = lp + dnx + 1; w2 = w1 + dnx; px = (int *)(w2 + dnx); /* Compute the weight vectors at each point in X. */ for (i=0; i < dnx; i++) { x = x_src[i] - 0.5; px[i] = (int) x; w1[i] = 1.0 - (x - (int)x); w2[i] = 1.0 - w1[i]; } /* For each line of the destination rect first interpolate in Y to the * y_src coordinate of the output line, then interpolate in X to compute * the final output pixels. */ for (j=0; j < dny; j++) { op = odata + (j+dy) * obpl + dx; y = y_src[j] - 0.5; line = (int) y; lo = idata + line * ibpl; hi = idata + min (iny - 1, line + 1) * ibpl; lo_w = 1.0 - (y - line); hi_w = 1.0 - lo_w; /* Interpolate in Y to the line at y_src[j]. */ for (i=0; i < dnx; i++) { pixel = px[i]; lp[i] = (float)lo[pixel] * lo_w + (float)hi[pixel] * hi_w; } lp[-1] = lp[0]; lp[dnx] = lp[dnx-1]; /* Interpolate in X to the final output pixels. */ if (!clip_region) { for (i=0; i < dnx; i++) op[i] = lp[i] * w1[i] + lp[i+1] * w2[i]; } else { for (i=0; i < dnx; i++) if (XPointInRegion (clip_region, i + dx, j + dy)) op[i] = lp[i] * w1[i] + lp[i+1] * w2[i]; } } XtFree ((char *)buf); } /* scale_lowpass -- Apply a lowpass filter to a region of a 2D data array. * The region ox,oy,nx,ny of the output data array is calculated by running * a convolution kernel over the region of the input data array at ix,iy. * The size of the convolution kernel is adjusted to match the scale factors * xscale, yscale. */ static scale_lowpass (idata,inx,iny,ibpl, odata,onx,ony,obpl, x_src,y_src, sx,sy,snx,sny, dx,dy,dnx,dny, xscale,yscale, clip_region) uchar *idata, *odata; /* input, output data */ int inx, iny, ibpl; /* full input array */ int onx, ony, obpl; /* full input array */ float *x_src, *y_src; /* src coords of each dst pixel */ int sx, sy, snx, sny; /* source rect */ int dx, dy, dnx, dny; /* destination rect */ float xscale, yscale; /* scale factors */ Region clip_region; /* clip Region or null */ { uchar *data; if ((data = (uchar *) XtMalloc (inx * iny)) == NULL) return; /* Run a lowpass filter over the input rect. */ lw_convolve (idata,inx,iny,ibpl, sx,sy, data,inx,iny,ibpl, sx,sy, snx,sny, xscale,yscale); /* Sample the filtered data to generate the output rect. */ scale_nearest (data,inx,iny,ibpl, odata,onx,ony,obpl, x_src,y_src, dx,dy,dnx,dny, clip_region); XtFree ((char *)data); } /* lw_convolve -- Convolution primitive for scale_lowpass. */ static lw_convolve (idata,inx,iny,ibpl,ix,iy, odata,onx,ony,obpl,ox,oy, nx, ny, xscale, yscale) uchar *idata, *odata; /* input, output data */ int inx, iny, ix, iy; /* size of input array, start pos */ int onx, ony, ox, oy; /* size of output array, start pos */ int ibpl, obpl; /* bytes per line */ int nx, ny; /* size of output region */ float xscale, yscale; /* determines amount of smoothing */ { register uchar *ip; register int l, m, x, hx, pixval; int kx, ky, hy, i, j, y; uchar *lp[11], *op; /* Determine kernel size (min 3x3, max 7x7). */ if (xscale < 0.1) hx = 3; else if (xscale >= 0.5) hx = 1; else hx = ((int)(1.0 / xscale)) / 2; if (yscale < 0.1) hy = 3; else if (yscale >= 0.5) hy = 1; else hy = ((int)(1.0 / yscale)) / 2; kx = hx * 2 + 1; ky = hy * 2 + 1; /* Compute the output data. */ for (j=0; j < ny; j++) { /* Get line pointers. */ op = odata + (j+oy) * obpl + ox; for (i=0; i < ky; i++) { y = iy + j - hy + i; if (y < 0) y = 0; else if (y >= iny) y = iny - 1; lp[i] = y * ibpl + idata; } /* Compute a line of output pixels */ for (i=0; i < nx; i++) { x = ix + i; pixval = 0; if (x < hx) { /* Near left edge. */ for (m=0; m < ky; m++) for (l=0; l < kx; l++) pixval += lp[m][max(0,x-hx+l)]; } else if (x >= inx - hx) { /* Near right edge. */ for (m=0; m < ky; m++) for (l=0; l < kx; l++) pixval += lp[m][min(inx-1,x-hx+l)]; } else { /* In central region. */ for (m=0; m < ky; m++) { ip = lp[m] + x - hx; for (l=0; l < kx; l++) pixval += ip[l]; } } op[i] = (float)pixval / (float)(kx * ky) + 0.5; } } } /* scale_boxcar -- Apply a boxcar filter to a region of a 2D data array * and interpolate the result to the output grid. The region ox,oy,nx,ny of * the output data array is calculated by block averaging the corresponding * source rect and then sampling the reduced image using bilinear interpolation * to compute the output pixel raster. This antialiasing technique aims to * be as fast as possible but still does a reasonable job of reducing the * image. */ static scale_boxcar (idata,inx,iny,ibpl, odata,onx,ony,obpl, x_src,y_src, sx,sy,snx,sny, dx,dy,dnx,dny, xscale,yscale, interp, clip_region) uchar *idata, *odata; /* input, output data */ int inx, iny, ibpl; /* full input array */ int onx, ony, obpl; /* full input array */ float *x_src, *y_src; /* src coords of each dst pixel */ int sx, sy, snx, sny; /* source rect */ int dx, dy, dnx, dny; /* destination rect */ float xscale, yscale; /* scale factors */ int interp; /* set if interpolation is desired */ Region clip_region; /* clip Region or null */ { int xblock, yblock; int x1, x2, y1, y2, nx, ny; float xstep, ystep; int xoff, yoff; uchar *bp; /* Determine blocking factors. If interpolation is disabled we need * to block one step more than for the linear interpolate case in order * to ensure that all the data is used. */ xblock = max(1, (int) (1.0 / xscale)); if (!interp && (1.0 / xscale) - xblock > ZOOM_TOL) xblock++; yblock = max(1, (int) (1.0 / yscale)); if (!interp && (1.0 / yscale) - yblock > ZOOM_TOL) yblock++; /* Align the input region for the given blocking factors. */ x1 = sx / xblock * xblock; x2 = (sx + snx - 1) / xblock * xblock; y1 = sy / yblock * yblock; y2 = (sy + sny - 1) / yblock * yblock; nx = (x2 - x1) / xblock + 1; ny = (y2 - y1) / yblock + 1; /* Compute the block averaged input rect. */ if (xblock > 1 || yblock > 1) { if ((bp = (uchar *) XtMalloc (nx * ny)) == NULL) return; bx_boxcar (idata,inx,iny,ibpl, x1,y1,x2,y2, bp, xblock, yblock); idata = bp; inx = ibpl = nx; iny = ny; xoff = x1; yoff = y1; xstep = 1.0 / xblock; ystep = 1.0 / yblock; } else { bp = NULL; xoff = yoff = 0; xstep = ystep = 1.0; } /* Interpolate the input rect to the output grid. */ if (interp && ((1.0 / xscale) - xblock) > ZOOM_TOL || ((1.0 / yscale) - yblock) > ZOOM_TOL) { /* Use bilinear interpolation to compute the output grid. */ bx_interp (idata,inx,iny,ibpl, odata,onx,ony,obpl, x_src,y_src, xoff,yoff,xstep,ystep, dx,dy,dnx,dny, clip_region); } else { /* Extract pixels from block averaged input data. */ bx_extract (idata,inx,iny,ibpl, odata,onx,ony,obpl, x_src,y_src, xoff,yoff,xstep,ystep, dx,dy,dnx,dny, clip_region); } if (bp) XtFree ((char *)bp); } /* bx_boxcar -- Block average primitive for scale_boxcar. */ static bx_boxcar (idata,inx,iny,ibpl, x1,y1,x2,y2, obuf, xblock, yblock) uchar *idata; /* input data array */ int inx, iny, ibpl; /* array dimensions */ int x1,y1,x2,y2; /* region to be block averaged */ uchar *obuf; /* output array */ int xblock, yblock; /* blocking factors */ { register uchar *ip, *op; register int count, i, *sp; int obpl, block, nxblocks, nyblocks, j, k; uchar *lp, *bp; int *sb; nxblocks = obpl = (x2 - x1) / xblock + 1; nyblocks = (y2 - y1) / yblock + 1; count = xblock * yblock; if ((sb = (int *) XtMalloc (obpl * sizeof(int))) == NULL) return; /* I don't think the following works for pixel values allocated from the * default colormap, as the pixel values are not sequentially allocated. */ for (block = 0; block < nyblocks; block++) { lp = idata + ibpl * (block * yblock + y1) + x1; bp = obuf + block * obpl; memset (sb, 0, obpl * sizeof(int)); for (k=yblock; --k >= 0; lp += ibpl) for (j=nxblocks, ip=lp, sp=sb; --j >= 0; sp++) for (i=xblock; --i >= 0; ) *sp += *ip++; for (i=obpl, sp=sb, op=bp; --i >= 0; ) *op++ = *sp++ / count; } XtFree ((char *)sb); } /* bx_extract -- Block extract primitive for scale_boxcar. */ static bx_extract (idata,inx,iny,ibpl, odata,onx,ony,obpl, x_src,y_src, xoff,yoff,xstep,ystep, dx,dy,dnx,dny, clip_region) uchar *idata, *odata; /* input, output data */ int inx, iny, ibpl; /* full input array */ int onx, ony, obpl; /* full input array */ float *x_src, *y_src; /* src coords of each dst pixel */ int dx, dy, dnx, dny; /* destination rect */ int xoff, yoff; /* offset of input region */ float xstep, ystep; /* scale of input region */ Region clip_region; /* clip Region or null */ { register int m, n, i; register uchar *op; int j; for (j=0; j < dny; j++) { op = odata + (j+dy) * obpl + dx; n = (y_src[j] - yoff) * ystep; if (!clip_region) { for (i=0; i < dnx; i++) { m = (x_src[i] - xoff) * xstep; op[i] = idata[n * ibpl + m]; } } else { for (i=0; i < dnx; i++) if (XPointInRegion (clip_region, i + dx, j + dy)) { m = (x_src[i] - xoff) * xstep; op[i] = idata[n * ibpl + m]; } } } } /* bx_interp -- Bilinear interpolation primitive for scale_boxcar. */ static bx_interp (idata,inx,iny,ibpl, odata,onx,ony,obpl, x_src,y_src, xoff,yoff,xstep,ystep, dx,dy,dnx,dny, clip_region) uchar *idata, *odata; /* input, output data */ int inx, iny, ibpl; /* dimensions of input array */ int onx, ony, obpl; /* dimensions of output array */ float *x_src, *y_src; /* src coords of each dst pixel */ int xoff, yoff; /* offset of input region */ float xstep, ystep; /* scale of input region */ int dx, dy, dnx, dny; /* destination rect */ Region clip_region; /* clip Region or null */ { register int i; register uchar *op; register float *lp, *w1, *w2; int buflen, line, *px, pixel, j; float lo_w, hi_w, x, y; uchar *lo, *hi; float *buf; buflen = (3 * dnx + 2) * sizeof(float) + dnx * sizeof(int); if ((buf = (float *) XtMalloc (buflen)) == NULL) return; lp = buf + 1; w1 = lp + dnx + 1; w2 = w1 + dnx; px = (int *)(w2 + dnx); /* Compute the weight vectors at each point in X. */ for (i=0; i < dnx; i++) { x = ((x_src[i] - xoff) * xstep) - 0.5; px[i] = (int) x; w1[i] = 1.0 - (x - (int)x); w2[i] = 1.0 - w1[i]; } /* For each line of the destination rect first interpolate in Y to the * y_src coordinate of the output line, then interpolate in X to compute * the final output pixels. */ for (j=0; j < dny; j++) { op = odata + (j+dy) * obpl + dx; y = ((y_src[j] - yoff) * ystep) - 0.5; line = (int) y; lo = idata + line * ibpl; hi = idata + min (iny - 1, line + 1) * ibpl; lo_w = 1.0 - (y - line); hi_w = 1.0 - lo_w; /* Interpolate in Y to the line at y_src[j]. */ for (i=0; i < dnx; i++) { pixel = px[i]; lp[i] = (float)lo[pixel] * lo_w + (float)hi[pixel] * hi_w; } lp[-1] = lp[0]; lp[dnx] = lp[dnx-1]; /* Interpolate in X to the final output pixels. */ if (!clip_region) { for (i=0; i < dnx; i++) op[i] = lp[i] * w1[i] + lp[i+1] * w2[i]; } else { for (i=0; i < dnx; i++) if (XPointInRegion (clip_region, i + dx, j + dy)) op[i] = lp[i] * w1[i] + lp[i+1] * w2[i]; } } XtFree ((char *)buf); } /* mf_getinten -- Copy the source rect to the destination rect, converting * pixel numbers to pixel intensities. */ static mf_getinten (w, idata,inx,iny,ibpl, sx,sy, odata,onx,ony,obpl, dx,dy, nx,ny) GtermWidget w; uchar *idata, *odata; /* input, output data */ int inx, iny, ibpl; /* dimensions of input array */ int onx, ony, obpl; /* dimensions of output array */ int sx, sy; /* source offset */ int dx, dy; /* destination offset */ int nx, ny; /* size of region */ { register Pixel *cmap; register uchar *ip, *op; register int n; int j; cmap = get_cmap_out (w); for (j=0; j < ny; j++) { ip = idata + ibpl * (sy + j) + sx; op = odata + obpl * (dy + j) + dx; for (n = nx; --n >= 0; ) *op++ = (cmap[*ip++]); } } /* mf_getpixel -- Copy the source rect to the destination rect, converting * pixel intensities to pixel numbers. */ static mf_getpixel (w, idata,inx,iny,ibpl, sx,sy, odata,onx,ony,obpl, dx,dy, nx,ny) GtermWidget w; uchar *idata, *odata; /* input, output data */ int inx, iny, ibpl; /* dimensions of input array */ int onx, ony, obpl; /* dimensions of output array */ int sx, sy; /* source offset */ int dx, dy; /* destination offset */ int nx, ny; /* size of region */ { register Pixel *cmap; register uchar *ip, *op; register int n; int j; cmap = get_cmap_in (w); for (j=0; j < ny; j++) { ip = idata + ibpl * (sy + j) + sx; op = odata + obpl * (dy + j) + dx; for (n = nx; --n >= 0; ) *op++ = (cmap[*ip++]); } } /* get_regions -- For each pixel I in the sequence of DNX pixels starting at DX * there is an associated value XMAP[I]. Compare this sequence to an alternate * sequence and return a list of subregions {XS,XE,XV} for which the XMAP * values are equal (XV=0), not equal (XV=1), or not common to (XV=2) the two * regions. The number of regions output is returned as the function value. */ static get_regions (xs,xe,xv, max_regions, dx, dnx, xmap, alt_dx, alt_dnx, alt_xmap) int *xs, *xe, *xv, max_regions; int dx, dnx, *xmap; int alt_dx, alt_dnx, *alt_xmap; { register int state, current; register int nx, i; int offset, old_i; offset = dx - alt_dx; nx = 0; for (i=0; i < dnx; i++) { if (nx >= max_regions-1) return (0); /* Determine whether or not this pixel is mapped the same in both * sequences. */ old_i = i + offset; if (alt_dnx <= 0 || old_i < 0 || old_i >= alt_dnx) state = 2; else state = (xmap[i] != alt_xmap[old_i]); /* When the state boundary is crossed add a range. */ if (i == 0) { xs[nx] = dx; xv[nx] = current = state; } if (state != current) { xe[nx] = dx + i - 1; xs[++nx] = dx + i; xv[nx] = current = state; } if (i == dnx-1) xe[nx++] = dx + i; } return (nx); } /* get_rects -- Combine two lists of regions, one in X and the other in Y, * to produce a list of 2D rectangles defining the regions outlined by the * region lists. Only rects for which the given condition is true in either * X or Y are selected. Adjacent rects are combined. */ static get_rects (o_rl, max_rects, xs,xe,xv,nx, ys,ye,yv,ny, xcond,ycond) XRectangle *o_rl; /* receives list of rectangles */ int max_rects; /* max rectangles out */ int *xs, *xe, *xv, nx; /* X list of regions */ int *ys, *ye, *yv, ny; /* Y list of regions */ int xcond, ycond; /* X,Y condition bitflags */ { register int i, j; XRectangle rl[MAX_REGIONS]; int limit = min (max_rects, MAX_REGIONS); int o_nrects=0, nrects=0; int i1, i2, j1, j2; /* Get initial list of rects matching the given X and Y conditions. * Rects which are adjacent in X are combined to form one larger rect. */ for (j=0; j < ny; j++) { for (i=0; i < nx; i++) { if ((xv[i] & xcond) || (yv[j] & ycond)) { /* Collapse rects adjacent in X. */ for (i1 = i2 = i++; i < nx; i++) { if ((xv[i] & xcond) || (yv[j] & ycond)) i2 = i; else break; } rl[nrects].x = xs[i1]; rl[nrects].y = ys[j]; rl[nrects].width = xe[i2] - xs[i1] + 1; rl[nrects].height = ye[j] - ys[j] + 1; if (++nrects >= limit) return (nrects); } } } /* Now scan the rect list and collapse rects which are adjacent in Y * into one larger rect. Find all the rects that are at the same X * and write them to the output list, collapsing rects that are adjacent * in Y in the process. */ for (i=0; i < nx; i++) for (j=0; j < nrects; ) { /* Find first rect if any. */ for (j1=0, j2 = -1; j < nrects; j++) if (rl[j].x == xs[i]) { j1 = j2 = j++; break; } /* Collapse rects adjacent in Y. */ for ( ; j < nrects; j++) if (rl[j].x == xs[i]) if (rl[j].y == rl[j2].y + rl[j2].height && rl[j].width == rl[j1].width) j2 = j; else break; /* Output the rect. */ if (j2 >= j1) { o_rl[o_nrects].x = rl[j1].x; o_rl[o_nrects].y = rl[j1].y; o_rl[o_nrects].width = rl[j1].width; o_rl[o_nrects].height = rl[j2].y + rl[j2].height - rl[j1].y; if (++o_nrects >= max_rects) return (o_nrects); } } return (o_nrects); } /* rect_intersect -- Compute the intersection of two rects. The area of * the intersection is returned as the function value, i.e., zero is * returned if the rects do not intersect. */ static rect_intersect (in, r1, r2) register XRectangle *in; register XRectangle *r1, *r2; { int x1, y1, x2, y2; x1 = max (r1->x, r2->x); y1 = max (r1->y, r2->y); x2 = min ((int)r1->x + (int)r1->width, (int)r2->x + (int)r2->width) - 1; y2 = min ((int)r1->y + (int)r1->height, (int)r2->y + (int)r2->height) - 1; in->x = x1; in->y = y1; in->width = max (0, x2 - x1 + 1); in->height = max (0, y2 - y1 + 1); return (in->width * in->height); } /* save_mapping -- Store a mapping in a mapping descriptor. */ static save_mapping (mp, mapping, rop, src, st, sx,sy,sw,sh, dst, dt, dx,dy,dw,dh) register Mapping mp; int mapping, rop; int src, st, sx,sy,sw,sh; int dst, dt, dx,dy,dw,dh; { mp->src = src; mp->st = st; mp->sx = sx; mp->sy = sy; mp->snx = sw; mp->sny = sh; mp->dst = dst; mp->dt = dt; mp->dx = dx; mp->dy = dy; mp->dnx = dw; mp->dny = dh; mp->defined = mp->enabled = mp->refresh = 1; mp->mapping = mapping; mp->rop = rop; } /* load_mapping -- Load a mapping from a mapping descriptor. */ static load_mapping (mp, mapping, rop, src, st, sx,sy,sw,sh, dst, dt, dx,dy,dw,dh) register Mapping mp; int *mapping, *rop; int *src, *st, *sx,*sy,*sw,*sh; int *dst, *dt, *dx,*dy,*dw,*dh; { *src = mp->src; *st = mp->st; *sx = mp->sx; *sy = mp->sy; *sw = mp->snx; *sh = mp->sny; *dst = mp->dst; *dt = mp->dt; *dx = mp->dx; *dy = mp->dy; *dw = mp->dnx; *dh = mp->dny; *mapping = mp->mapping; *rop = mp->rop; } /* get_pixel_mapping -- Copy a mapping, converting to pixel coordinates in * the process if the mapping is not already in pixel coordinates. */ static get_pixel_mapping (w, mp1, mp2, update) GtermWidget w; register Mapping mp1; /* input mapping */ register Mapping mp2; /* output mapping */ int update; /* update mapping */ { float maxndc = (float)MAXNDC; mp2->mapping = mp1->mapping; mp2->refresh = mp1->refresh; mp2->enabled = mp1->enabled; mp2->rop = mp1->rop; if (!(mp2->defined = mp1->defined)) return; mp2->src = mp1->src; if (mp1->st == GtPixel) { mp2->st = mp1->st; mp2->sx = mp1->sx; mp2->sy = mp1->sy; mp2->snx = mp1->snx; mp2->sny = mp1->sny; } else { float width = w->gterm.rasters[mp1->src].width; float height = w->gterm.rasters[mp1->src].height; mp2->sx = mp1->sx * width / maxndc; mp2->sy = (MAXNDC - (mp1->sy + abs(mp1->sny))) * height / maxndc; mp2->snx = mp1->snx * width / maxndc; mp2->sny = mp1->sny * height / maxndc; /* NDC flipped in Y */ mp2->st = GtPixel; } mp2->dst = mp1->dst; if (mp1->dt == GtPixel) { mp2->dt = mp1->dt; mp2->dx = mp1->dx; mp2->dy = mp1->dy; mp2->dnx = mp1->dnx; mp2->dny = mp1->dny; } else { float width = w->gterm.rasters[mp1->dst].width; float height = w->gterm.rasters[mp1->dst].height; mp2->dx = mp1->dx * width / maxndc; mp2->dy = (MAXNDC - (mp1->dy + abs(mp1->dny))) * height / maxndc; mp2->dnx = mp1->dnx * width / maxndc; mp2->dny = mp1->dny * -height / maxndc; /* NDC flipped in Y */ mp2->dt = GtPixel; } /* The lookup tables are already in pixel space, so we can propagate * these to the new mapping if the old mapping was updated. */ if (update && mp1->updated) { if (mp2->mapdata = (uchar *) XtMalloc (mp1->datalen)) { memmove (mp2->mapdata, mp1->mapdata, mp1->datalen); mp2->datalen = mp1->datalen; mp2->scaling = mp1->scaling; mp2->xscale = mp1->xscale; mp2->yscale = mp1->yscale; mp2->x_extent = (mapExtent *) ((uchar *)mp1->x_extent - mp1->mapdata + mp2->mapdata); mp2->y_extent = (mapExtent *) ((uchar *)mp1->y_extent - mp1->mapdata + mp2->mapdata); mp2->x_srcpix = (int *) ((uchar *)mp1->x_srcpix - mp1->mapdata + mp2->mapdata); mp2->y_srcpix = (int *) ((uchar *)mp1->y_srcpix - mp1->mapdata + mp2->mapdata); mp2->x_src = (float *) ((uchar *)mp1->x_src - mp1->mapdata + mp2->mapdata); mp2->y_src = (float *) ((uchar *)mp1->y_src - mp1->mapdata + mp2->mapdata); mp2->updated = 1; } } else mp2->updated = 0; } /* valid_mapping -- Perform some sanity checks on a mapping to verify that * it contains something meaningful. */ static valid_mapping (w, mp) GtermWidget w; register Mapping mp; { register int x, y; int snx, sny, dnx, dny; int s_width, s_height, d_width, d_height; Raster sr, dr; if (!mp || !mp->defined) return (False); if (mp->src < 0 || mp->src >= w->gterm.maxRasters) return (False); if (mp->dst < 0 || mp->dst >= w->gterm.maxRasters) return (False); sr = &w->gterm.rasters[mp->src]; dr = &w->gterm.rasters[mp->dst]; if (!sr->type || !dr->type) return (False); switch (mp->st) { case GtPixel: s_width = sr->width; s_height = sr->height; break; case GtNDC: s_width = MAXNDC + 1; s_height = MAXNDC + 1; break; default: return (False); } switch (mp->dt) { case GtPixel: d_width = dr->width; d_height = dr->height; break; case GtNDC: d_width = MAXNDC + 1; d_height = MAXNDC + 1; break; default: return (False); } snx = mp->snx; dnx = abs(mp->dnx); sny = mp->sny; dny = abs(mp->dny); if (snx <= 0 || dnx <= 0 || sny <= 0 || dny <= 0) return (False); x = mp->sx; y = mp->sy; if (x < 0 || x >= s_width || y < 0 || y >= s_height) return (False); x = mp->sx + snx - 1; y = mp->sy + sny - 1; if (x < 0 || x >= s_width || y < 0 || y >= s_height) return (False); x = mp->dx; y = mp->dy; if (x < 0 || x >= d_width || y < 0 || y >= d_height) return (False); x = mp->dx + dnx - 1; y = mp->dy + dny - 1; if (x < 0 || x >= d_width || y < 0 || y >= d_height) return (False); return (True); } /* initialize_mapping -- Initialize the contents of a mapping descriptor. */ static initialize_mapping (mp) register Mapping mp; { memset ((char *)mp, 0, sizeof(struct mapping)); } /* update_mapping -- Update the portion of a mapping descriptor used at * runtime to execute the mapping. This information consists of several * lookup tables and other parameters describing how a destination pixel * maps back to a source pixel and vice versa. */ static update_mapping (w, mp) GtermWidget w; register Mapping mp; { register uchar *op; register int i, j, k; int snx, sny, dnx, dny, sx, sy, dx, dy; int xmax, ymax, lo, hi, edge1, edge2; int temp, xflip=0, yflip=0; struct mapping p_mp; float pixwidth, *fp; int *ip; if (mp->updated) return; /* The internal lookup tables are in pixel units. */ initialize_mapping (&p_mp); get_pixel_mapping (w, mp, &p_mp, 0); if ((snx = p_mp.snx) <= 0 || (sny = p_mp.sny) <= 0) return; if ((dnx = p_mp.dnx) < 0) { dnx = -dnx; xflip++; } if ((dny = p_mp.dny) < 0) { dny = -dny; yflip++; } sx = p_mp.sx; sy = p_mp.sy; dx = p_mp.dx; dy = p_mp.dy; xmax = dnx - 1; ymax = dny - 1; /* Get scale factors. */ mp->xscale = (float)dnx / (float)snx; mp->yscale = (float)dny / (float)sny; /* Determine type of scaling to be used. */ if (mp->xscale < 1.0 || mp->yscale < 1.0) { mp->scaling = M_DEZOOM; } else if (mp->xscale > 1.0 || mp->yscale > 1.0) { mp->scaling = M_ZOOM; if (abs(mp->xscale - (int)(mp->xscale+0.5)) < ZOOM_TOL && abs(mp->yscale - (int)(mp->yscale+0.5)) < ZOOM_TOL) mp->scaling = M_INTZOOM; } else mp->scaling = (xflip || yflip) ? M_ZOOM : M_NOSCALING; /* Get a data buffer for the lookup tables. */ mp->datalen = snx * sizeof(mapExtent) + /* xy, extent */ sny * sizeof(mapExtent) + dnx * sizeof(int) + /* xy, srcpix */ dny * sizeof(int) + dnx * sizeof(float) + /* xy, src */ dny * sizeof(float); if (mp->mapdata) mp->mapdata = (uchar *) XtRealloc ((char *)mp->mapdata, mp->datalen); else mp->mapdata = (uchar *) XtMalloc (mp->datalen); if (mp->mapdata == NULL) return; /* Set the table pointers. */ op = mp->mapdata; mp->x_extent = (mapExtent *) op; op += snx * sizeof(mapExtent); mp->y_extent = (mapExtent *) op; op += sny * sizeof(mapExtent); mp->x_srcpix = (int *) op; op += dnx * sizeof(int); mp->y_srcpix = (int *) op; op += dny * sizeof(int); mp->x_src = (float *) op; op += dnx * sizeof(float); mp->y_src = (float *) op; op += dny * sizeof(float); /* Compute the backpointers to the source raster for each destination * pixel center. */ for (i=0, ip = mp->x_srcpix, fp = mp->x_src; i < dnx; i++) { fp[i] = ((xflip ? xmax - i : i) + 0.5) / mp->xscale + sx; ip[i] = fp[i]; } for (i=0, ip = mp->y_srcpix, fp = mp->y_src; i < dny; i++) { fp[i] = ((yflip ? ymax - i : i) + 0.5) / mp->yscale + sy; ip[i] = fp[i]; } /* Compute the extent arrays. These define the range of destination * pixels affected by each source pixel. */ lo = dnx - 1 + dx; hi = dx; for (i=0; i < snx; i++) { mp->x_extent[i].lo = lo; mp->x_extent[i].hi = hi; } lo = dny - 1 + dy; hi = dy; for (i=0; i < sny; i++) { mp->y_extent[i].lo = lo; mp->y_extent[i].hi = hi; } /* Map the left and right or top and bottom edges of each destination * pixel back into the source rect and update the corresponding extent * entries to indicate that these source pixels are used to compute the * destination pixel. */ pixwidth = 1.0 - ZOOM_TOL; for (i=0; i < dnx; i++) { edge1 = (xflip ? xmax - i : i) / mp->xscale; edge2 = (xflip ? xmax - (i-pixwidth) : (i+pixwidth)) / mp->xscale; if (edge1 > edge2) { temp = edge1; edge1 = edge2; edge2 = temp; } edge1 = max (0, edge1); edge2 = min (snx - 1, edge2); for (j=edge1, k = dx + i; j <= edge2; j++) { if (mp->x_extent[j].lo > k) mp->x_extent[j].lo = k; if (mp->x_extent[j].hi < k) mp->x_extent[j].hi = k; } } for (i=0; i < dny; i++) { edge1 = (yflip ? ymax - i : i) / mp->yscale; edge2 = (yflip ? ymax - (i-pixwidth) : (i+pixwidth)) / mp->yscale; if (edge1 > edge2) { temp = edge1; edge1 = edge2; edge2 = temp; } edge1 = max (0, edge1); edge2 = min (sny - 1, edge2); for (j=edge1, k = dy + i; j <= edge2; j++) { if (mp->y_extent[j].lo > k) mp->y_extent[j].lo = k; if (mp->y_extent[j].hi < k) mp->y_extent[j].hi = k; } } mp->updated = 1; } /* free_mapping -- Free any storage used internally by a mapping descriptor, * and deactivate the mapping. */ static free_mapping (w, mp) GtermWidget w; register Mapping mp; { mp_unlink (w, mp); mp->defined = mp->enabled = mp->updated = 0; if (mp->mapdata) { XtFree ((char *) mp->mapdata); mp->mapdata = NULL; mp->datalen = 0; mp->x_extent = mp->y_extent = NULL; mp->x_srcpix = mp->y_srcpix = NULL; mp->x_src = mp->y_src = NULL; mp->updated = 0; } } static void mp_linkafter (w, mp, ref_mp) register GtermWidget w; register Mapping mp; register Mapping ref_mp; { register Mapping map; /* Don't use the reference mapping unless it is already linked or * the list is empty. */ if (w->gterm.mp_head) { for (map = w->gterm.mp_head; map && map != ref_mp; map = map->next) ; if (map != ref_mp) ref_mp = NULL; } mp->prev = ref_mp; mp->next = ref_mp ? ref_mp->next : NULL; if (ref_mp && ref_mp->next) ref_mp->next->prev = mp; if (ref_mp) ref_mp->next = mp; if (!w->gterm.mp_tail || ref_mp == w->gterm.mp_tail) w->gterm.mp_tail = mp; if (!w->gterm.mp_head) w->gterm.mp_head = mp; } static void mp_unlink (w, mp) register GtermWidget w; register Mapping mp; { if (mp->prev) mp->prev->next = mp->next; if (mp->next) mp->next->prev = mp->prev; if (w->gterm.mp_head == mp) w->gterm.mp_head = mp->next; if (w->gterm.mp_tail == mp) w->gterm.mp_tail = mp->prev; mp->prev = mp->next = NULL; } /* * Graphics MARKERS. * -------------------- * A marker is an active graphics object displayed on top of a drawing to * mark, annotate, or outline a region. Markers can respond to events and * move, resize, or modify themselves, optionally executing callback * procedures when the marker changes state. Markers are used to * interactively define regions with the mouse, to provide a dynamic graphical * display which doesn't interfere with the underlying graphics frame, or as a * graphical means of command input, using callbacks to perform some operation * when the marker is moved or resized by the user. * * GtMarkerInit (w) * GtMarkerFree (w) * * gm = GmCreate (w, type, interactive) * GmRedisplay (w, region|NULL) * gm = GmCopy (gm) * GmDestroy (gm) * GmAddCallback (gm, events, func, client_data) * GmDeleteCallback (gm, func, client_data) * gm = GmSelect (gt, x, y, &what) * * GmMarkpos (gm) * GmRedraw (gm, func, erase) * GmRaise (gm, ref_gm|NULL) * GmLower (gm, ref_gm|NULL) * GmNotify (gm, events, event, param, nparams) * * GmAddPt (gm, x, y) * GmDeletePt (gm, x, y) * GmMovePt (gm, x, y) * GmMove (gm, x, y) * GmResize (gm, x, y) * GmRotate (gm, x, y) * * GmSetAttributes (gm, args, nargs, type) * GmGetAttributes (gm, args, nargs, type) * GmSetAttribute (gm, attribute, value, type) * GmGetAttribute (gm, attribute, value, type) * GmSetVertices (gm, points, first, npts) * npts = GmGetVertices (gm, points, first, maxpts) * GmGetBoundingBox (gm, x, y, width, height) * * type = GmStrToType (marker_type) * event = GmStrToEvent (event_type) * func = GmStrToFunction (drawing_function) * * Markers operate in screen coordinates (raster 0). The SelectRaster * and MapVector routines may be used to convert to and from raster * coordinates if desired. * * raster = GtSelectRaster (gt, dras, dt, dx, dy, rt, &rx, &ry, &mp) * GtMapVector (gt, mp, dir, st, sv, dt, dv, npts, clip) * * The Gm procedures above implement the main functionality of markers. User * interaction is provided at a higher level using action procedures which * are bound to pointer and keyboard events via translations (or by the GUI * itself directly calling the above procedures). */ static void gm_text_init(), gm_line_init(), gm_plin_init(), gm_rect_init(); static void gm_boxx_init(), gm_circ_init(), gm_elip_init(), gm_pgon_init(); static int gm_putint(), gm_putfloat(), gm_do_callbacks(), gm_constraint(); static int gm_getint(), gm_getattribute(), gm_gettype(); static double gm_getfloat(); static char *gm_getstring(); static void gm_markpos(), gm_erase(), gm_redraw(), gm_setCurRect(); static void gm_linkafter(), gm_unlink(); static double gm_niceAngle(); static Pixel gm_getpixel(); static int gm_select(); static int gm_getfillstyle(); static GmVMethod gm_classinit[] = { gm_text_init, gm_line_init, gm_plin_init, gm_rect_init, gm_boxx_init, gm_circ_init, gm_elip_init, gm_pgon_init }; static Region null_region; static XRectangle null_rect = { 0, 0, 0, 0 }; #define NullRect(r) (!(r)->width || !(r)->height) #define PI_2 1.57079632679489661923 #define PI_4 0.78539816339744830962 #define BORDER 5 static void M_create(), M_destroy(), M_destroyNull(), M_set(), M_raise(); static void M_lower(), M_notify(), M_markpos(), M_markposAdd(), M_redraw(); static void M_addPt(), M_deletePt(), M_movePt(), M_deleteDestroy(); static void M_move(), M_resize(), M_moveResize(), M_rotate(); static void M_rotateResize(), M_input(); static void gm_focusin(), gm_focusout(); static XtActionsRec markerActionsList[] = { { "m_create", M_create }, { "m_destroy", M_destroy }, { "m_destroyNull", M_destroyNull }, { "m_set", M_set }, { "m_raise", M_raise }, { "m_lower", M_lower }, { "m_notify", M_notify }, { "m_input", M_input }, { "m_markpos", M_markpos }, { "m_markposAdd", M_markposAdd }, { "m_redraw", M_redraw }, { "m_addPt", M_addPt }, { "m_deletePt", M_deletePt }, { "m_movePt", M_movePt }, { "m_deleteDestroy", M_deleteDestroy }, { "m_move", M_move }, { "m_resize", M_resize }, { "m_moveResize", M_moveResize }, { "m_rotate", M_rotate }, { "m_rotateResize", M_rotateResize }, }; /* GtMarkerInit -- Initialize the marker subsystem. */ GtMarkerInit (w) GtermWidget w; { register Marker gm, prev; XColor fg_color, bg_color; Display *display = w->gterm.display; int type, i; GC gc; for (gm = w->gterm.gm_tail; gm; gm = prev) { prev = gm->prev; GmDestroy (gm); } if (!w->gterm.gm_initialized) { /* Register some additional actions for markers. */ XtAppAddActions (XtWidgetToApplicationContext((Widget)w), markerActionsList, XtNumber(markerActionsList)); /* Get the gterm widget translations. */ if ((char *)w->gterm.defTranslations == NULL) { char *translations = NULL; XtTranslations tt; XtResource r; int ttype, i; r.resource_name = XtNtranslations; r.resource_class = XtCTranslations; r.resource_type = XtRString; r.resource_size = sizeof (char *); r.resource_offset = 0; r.default_type = XtRString; r.default_addr = (caddr_t) NULL; XtGetApplicationResources ((Widget)w, &translations, &r, 1,NULL,0); if (translations) { if (strncmp (translations, "#augment", 8) == 0) ttype = T_augment; else if (strncmp (translations, "#override", 9) == 0) ttype = T_override; else ttype = T_replace; if (ttype == T_replace) { w->gterm.defTranslations = XtParseTranslationTable (translations); } else if ((i = w->gterm.nauxTrans) < MAX_AUXTRANS) { w->gterm.defTranslations = XtParseTranslationTable (defaultGtermTranslations); w->gterm.auxTrans[i] = XtParseTranslationTable (translations); w->gterm.auxTType[i] = ttype; w->gterm.nauxTrans++; } } else { w->gterm.defTranslations = XtParseTranslationTable (defaultGtermTranslations); } /* Get the marker translations. */ if ((char *)w->gterm.gm_defTranslations == NULL) w->gterm.gm_defTranslations = XtParseTranslationTable (w->gterm.gm_translations); } /* Cancel any load translation table interval timer. */ if (w->gterm.gm_timer_id) { XtRemoveTimeOut (w->gterm.gm_timer_id); w->gterm.gm_timer_id = (XtIntervalId) NULL; } /* Set the default gterm window translations. */ gm_load_translations (w, NULL); /* Get graphics drawing GC. */ gc = XCreateGC (display, w->gterm.root, 0, NULL); XSetBackground (display, gc, w->gterm.color0); XSetForeground (display, gc, w->gterm.color1); XSetLineAttributes (display, gc, w->gterm.gm_lineWidth, w->gterm.gm_lineStyle, CapButt, JoinMiter); w->gterm.gm_drawGC = gc; /* Get graphics rubber-band GC. */ gc = XCreateGC (display, w->gterm.root, 0, NULL); XSetFunction (display, gc, GXxor); XSetFillStyle (display, gc, FillSolid); XSetForeground (display, gc, w->gterm.gm_xorFillColor); XSetBackground (display, gc, w->gterm.gm_xorFillBgColor); XSetLineAttributes (display, gc, 0, LineDoubleDash, CapButt, JoinMiter); w->gterm.gm_rubberGC = gc; fg_color.pixel = w->gterm.gm_cursorFgColor; bg_color.pixel = w->gterm.gm_cursorBgColor; XQueryColor (display, w->core.colormap, &fg_color); XQueryColor (display, w->core.colormap, &bg_color); w->gterm.gm_markerCursor = XCreateFontCursor (display, XC_fleur); XRecolorCursor (display, w->gterm.gm_markerCursor, &fg_color,&bg_color); w->gterm.gm_edgeCursor = XCreateFontCursor (display, XC_dotbox); XRecolorCursor (display, w->gterm.gm_edgeCursor, &fg_color,&bg_color); w->gterm.gm_pointCursor = XCreateFontCursor (display, XC_sizing); XRecolorCursor (display, w->gterm.gm_pointCursor, &fg_color,&bg_color); if (!(type = GmStrToType (w->gterm.gm_defaultMarker))) type = Gm_Rectangle; w->gterm.gm_defaultType = type; if (!null_region) null_region = XCreateRegion(); w->gterm.gm_initialized++; } w->gterm.gm_create = NULL; w->gterm.gm_active = NULL; w->gterm.gm_redisplay = False; w->gterm.preserve_screen = 0; } /* GtMarkerFree -- Free any marker subsystem resources. */ static void GtMarkerFree (w) register GtermWidget w; { register Display *display = w->gterm.display; register Marker gm; /* Cancel any load translation table interval timer. */ if (w->gterm.gm_timer_id) { XtRemoveTimeOut (w->gterm.gm_timer_id); w->gterm.gm_timer_id = (XtIntervalId) NULL; } /* Set the default gterm window translations. */ gm_load_translations (w, NULL); for (gm = w->gterm.gm_tail; gm; gm = gm->prev) GmDestroy (gm); if (!w->gterm.gm_initialized) return; XFreeGC (display, w->gterm.gm_drawGC); XFreeGC (display, w->gterm.gm_rubberGC); /* This call can fail - see comments elsewhere in this file about * XFreeCursor. * XFreeCursor (display, w->gterm.gm_markerCursor); XFreeCursor (display, w->gterm.gm_edgeCursor); XFreeCursor (display, w->gterm.gm_pointCursor); */ w->gterm.gm_initialized = 0; } /* gm_focusin -- Called when gterm window input is directed to a marker. */ static void gm_focusin (w, gm, what) register GtermWidget w; register Marker gm; GmSelection what; { Cursor cursor; int erase; Marker am; if (!XtIsRealized ((Widget)w)) return; if (am = w->gterm.gm_active) { if (am != gm) gm_focusout (w, 0); else if (what && what->type == w->gterm.gm_selection.type) { /* no change */ return; } } if (what) { switch (what->type) { case Ge_Point: cursor = w->gterm.gm_pointCursor; break; case Ge_Edge: cursor = w->gterm.gm_edgeCursor; break; default: cursor = w->gterm.gm_markerCursor; break; } } else cursor = w->gterm.gm_markerCursor; erase_crosshair (w); XDefineCursor (w->gterm.display, w->gterm.window, cursor); w->gterm.gm_active = gm; w->gterm.gm_selection = *what; if (gm && gm != am) { gm_request_translations (w, gm); GmMarkpos (gm); GmRedraw (gm, GXcopy, erase=True); } gm_do_callbacks (gm, GmEvFocusIn, NULL, NULL, 0); } /* gm_focusout -- Called to restore the normal gterm window input when the * pointer moves off a marker. */ static void gm_focusout (w, enableSetTrans) register GtermWidget w; int enableSetTrans; /* replace translations */ { register Display *display = w->gterm.display; register Marker gm = w->gterm.gm_active; int erase, i; if (!XtIsRealized ((Widget)w)) return; /* Restore the default gterm window translations. */ if (enableSetTrans) gm_request_translations (w, NULL); XDefineCursor (display, w->gterm.window, w->gterm.cursor); w->gterm.gm_active = NULL; if (gm) { GmMarkpos (gm); GmRedraw (gm, GXcopy, erase=True); } gm_do_callbacks (gm, GmEvFocusOut, NULL, NULL, 0); } /* gm_refocus -- Simulate a pointer event to recompute the marker pointer * focus. Called when a software event changes the marker stacking order * in some way. */ static void gm_refocus (w) GtermWidget w; { XMotionEvent event; int nparams = 0; event.x = w->gterm.last_x; event.y = w->gterm.last_y; HandleTrackCursor ((Widget)w, &event, NULL, &nparams); } /* * Translation tables. The widget's translation table must not be replaced * while a translation is executing. This can be a problem as it is often * events and their translations which lead to the translation table getting * replaced. To avoid this problem we merely queue a timer event to load * the desired translation table, allowing any existing translation to * finish executing before the translation table is swapped out. If multiple * translation table load requests are issued only the final one has any * effect. */ /* gm_request_translations -- Queue a request to load the translations for the * specified marker (or NULL to load the default gterm translations). If this * is the first request and timers are enabled a timer is posted to load the * translations when any current event processing is complete. If a request * is already active then the most recent request supercedes any previous one. */ static void gm_request_translations (w, gm) register GtermWidget w; Marker gm; { w->gterm.gm_reqTranslations = gm; if (!w->gterm.useTimers) gm_load_translations (w, NULL); else if (!w->gterm.gm_timer_id) { w->gterm.gm_timer_id = XtAppAddTimeOut (XtWidgetToApplicationContext((Widget)w), 0, gm_load_translations, (XtPointer)w); } } /* gm_load_translations -- Swap out the widget's translation table. This is * a no-op if the requested translation table is already loaded. */ static void gm_load_translations (w, id) register GtermWidget w; XtIntervalId id; { register Marker am, gm; register int i; w->gterm.gm_timer_id = (XtIntervalId) NULL; am = w->gterm.gm_curTranslations; gm = w->gterm.gm_reqTranslations; if (am == gm && w->gterm.gm_initialized) return; if (gm) { /* Set the translations for the indicated marker. */ if (!am || am->translations != gm->translations) XtOverrideTranslations ((Widget)w, gm->translations); } else { /* Restore the default gterm window translations. */ XtVaSetValues ((Widget)w, XtNtranslations, (XtArgVal)w->gterm.defTranslations, NULL); for (i=0; i < w->gterm.nauxTrans; i++) { switch (w->gterm.auxTType[i]) { case T_augment: XtAugmentTranslations ((Widget)w, w->gterm.auxTrans[i]); break; case T_override: XtOverrideTranslations ((Widget)w, w->gterm.auxTrans[i]); break; } } } w->gterm.gm_curTranslations = w->gterm.gm_reqTranslations; } /* Public marker functions. * -------------------------- */ /* GmCreate -- Create a new marker. */ Marker GmCreate (w, type, interactive) GtermWidget w; int type; /* marker type */ int interactive; /* use pointer to set position */ { register Marker gm; /* Allocate descriptor. */ if (type < 1 || type > Gm_NTypes) return (NULL); if (!(gm = (Marker) XtCalloc (1, sizeof (struct marker)))) return (NULL); /* Initialize descriptor. */ gm->w = w; gm->type = type; gm->flags = interactive ? (Gm_Visible|Gm_Sensitive) : 0; gm->translations = w->gterm.gm_defTranslations; gm->old_region = XCreateRegion(); gm->cur_region = XCreateRegion(); (gm_classinit[type-1]) (gm, interactive); /* Link marker to tail of marker list. */ gm_linkafter (gm, w->gterm.gm_tail); /* If marker is being created interactive, set flag to indicate that the * next create marker event should finish creating this marker. */ if (w->gterm.gm_create) GmDestroy (w->gterm.gm_create); w->gterm.gm_create = interactive ? gm : NULL; return (gm); } /* GmDestroy -- Destroy a marker. */ GmDestroy (gm) register Marker gm; { register GtermWidget w = gm->w; Region old_region, cur_region; /* GmDestroy can be called recursively during a destroy operation as a * side effect of the destroy callback. Set the Gm_BeingDestroyed flag * to cause these redundant destroy requests to be ignored. */ if (gm->flags & Gm_BeingDestroyed) return (OK); gm->flags |= Gm_BeingDestroyed; /* Release the focus if active marker. This should be done before * proceeding to destroy the marker, i.e. before calling the destroy * callbacks. */ if (w->gterm.gm_active == gm) { gm_focusout (w, 1); w->gterm.gm_active = NULL; } /* Inform any clients that have registered a callback for this marker * that we are about to destroy the marker. */ gm_do_callbacks (gm, GmEvDestroy, NULL, NULL, 0); /* Erase the marker from the screen. */ GmMarkpos (gm); gm_erase (gm); /* Note marker position. */ old_region = gm->old_region; cur_region = gm->cur_region; /* Free all storage and unlink the marker. */ if (gm->npoints > GM_MAXVERTICES) XtFree ((char *)gm->points); if (gm->text) XtFree ((char *)gm->text); if (gm->pgon) XtFree ((char *)gm->pgon); gm_unlink (gm); XtFree ((char *)gm); /* Redraw any markers that were obscured by the deleted marker. */ update_transients (w, old_region); XDestroyRegion (old_region); XDestroyRegion (cur_region); /* Recompute the marker focus. */ gm_refocus (w); return (OK); } /* GmCopy -- Copy a marker. */ Marker GmCopy (gm) register Marker gm; { register GtermWidget w = gm->w; register Marker nm; if (!(nm = (Marker) XtCalloc (1, sizeof (struct marker)))) return (NULL); *nm = *gm; nm->parent = gm; nm->old_region = NULL; nm->cur_region = NULL; nm->points = NULL; nm->pgon = NULL; nm->text = NULL; /* Copy region descriptors. */ if ((char *)(nm->old_region = XCreateRegion()) == NULL) goto fail; if ((char *)(nm->cur_region = XCreateRegion()) == NULL) goto fail; XUnionRegion (nm->old_region, gm->cur_region, nm->cur_region); /* Copy any polypoint data. */ if (gm->pgon) { nm->pgon = (DPoint *) XtMalloc (gm->npoints * sizeof(DPoint)); if (nm->pgon == NULL) goto fail; memmove (nm->pgon, gm->pgon, gm->npoints * sizeof(DPoint)); } /* Copy region polygon. */ if (gm->npoints > GM_MAXVERTICES) { if (!(nm->points = (XPoint *) XtMalloc (gm->npoints * sizeof(XPoint)))) goto fail; memmove (nm->points, gm->points, gm->npoints * sizeof(XPoint)); } /* Copy any text data. */ if (gm->text) { int nchars = strlen (gm->text); if (!(nm->text = XtMalloc (nchars + 1))) goto fail; memmove (nm->text, gm->text, nchars + 1); } gm_linkafter (nm, w->gterm.gm_tail); return (nm); fail: if (nm->text) XtFree (nm->text); if (nm->pgon) XtFree ((char *)nm->pgon); if (nm->points && nm->points != nm->point_data) XtFree ((char *)nm->points); if ((char *)nm->cur_region) XDestroyRegion (nm->cur_region); if ((char *)nm->old_region) XDestroyRegion (nm->old_region); XtFree ((char *)nm); return (NULL); } /* GmAddCallback -- Add a callback to a marker. */ GmAddCallback (gm, events, func, client_data) register Marker gm; int events; /* events callback is to receive */ GmIMethod func; /* function to be called */ XtPointer client_data; /* client data for above */ { register struct markerCallback *cb; register int i; /* Find an empty callback slot. */ for (i=0; i < GM_MAXCALLBACKS; i++) if (!gm->callback[i].events) break; /* Register the callback. */ if (i < GM_MAXCALLBACKS) { cb = &gm->callback[i]; cb->events = events; cb->func = func; cb->client_data = client_data; gm->ncallbacks = max (gm->ncallbacks, i + 1); } if (events & GmEvConstraint) gm->constraints++; } /* GmDeleteCallback -- Delete a previously posted callback given the * function pointer and client data passed when the callback was registered. */ GmDeleteCallback (gm, func, client_data) register Marker gm; GmIMethod func; /* callback function */ XtPointer client_data; /* client data for above */ { register struct markerCallback *cb; register int i, n; for (i=n=0; i < GM_MAXCALLBACKS; i++) { cb = &gm->callback[i]; if (cb->func == func && cb->client_data == client_data) { if (cb->events & GmEvConstraint) gm->constraints--; cb->events = (int)NULL; cb->func = (GmIMethod)NULL; cb->client_data = (XtPointer)NULL; } else if (cb->events) n = i; } gm->ncallbacks = n + 1; } /* GmSelect -- Scan the marker list to see if the given pointer coordinates * are within an active marker. If so, the marker descriptor is returned as * the function value, and the "what" argument is set to indicate what part * of the marker was selected. */ Marker GmSelect (w, x, y, what) GtermWidget w; int x, y; GmSelection what; { register int flags = (Gm_Activated|Gm_Visible|Gm_Sensitive); register XRectangle *r; register Marker gm; for (gm = w->gterm.gm_tail; gm; gm = gm->prev) { if (!((gm->flags & (flags|Gm_BeingDestroyed)) == flags)) continue; r = &gm->cur_rect; if (x < (int)r->x || x >= (int)(r->x + r->width) || y < (int)r->y || y >= (int)(r->y + r->height)) continue; if (gm->select (gm, x, y, what)) return (gm); } return (NULL); } /* GmMarkpos -- Save the current marker position, e.g., prior to modifying * the marker. This is used to erase the old marker when the modified * marker is later redrawn. */ GmMarkpos (gm) register Marker gm; { gm->markpos (gm); } /* GmRedraw -- Redraw a marker using the given drawing function. If the erase * flag is not set (as when in rubber-band mode) the marker is merely drawn * to the screen. Otherwise if the old marker position has been saved the * old marker is first erased, then any markers affected by the erase are * redrawn, and finally the current marker is redrawn at the new location. */ GmRedraw (gm, func, erase) Marker gm; int func; int erase; { register Marker mm; register XRectangle *o, *n, *r; int flags = (Gm_Activated|Gm_Visible); Region clip_region, temp_region, temp; GtermWidget w = gm->w; int outside; /* Recompute marker polygon if any attributes have changed. */ gm->update (gm); clip_region = XCreateRegion(); temp_region = XCreateRegion(); /* Erase the previously marked region (old position). */ if (erase) { XUnionRegion (gm->old_region, clip_region, temp_region); temp = clip_region; clip_region = temp_region; temp_region = temp; XSetRegion (w->gterm.display, w->gterm.exposeGC, clip_region); gm_erase (gm); } if (!erase && func == GXxor) gm->redraw (gm, func); else { /* Draw the marker and any markers it intersects, clipping to the * new marker region. */ o = &gm->old_rect; n = &gm->cur_rect; XUnionRegion (gm->cur_region, clip_region, temp_region); temp = clip_region; clip_region = temp_region; temp_region = temp; XSetRegion (w->gterm.display, w->gterm.gm_drawGC, clip_region); for (mm = gm->w->gterm.gm_head; mm; mm = mm->next) { if (!((mm->flags & flags) == flags)) continue; /* Redraw a marker if it intersects either the old rect or the * new rect. */ if (mm != gm) { r = &mm->cur_rect; outside = 0; if ((int)r->x >= (int)o->x + (int)o->width || (int)r->x + (int)r->width <= (int)o->x || (int)r->y >= (int)o->y + (int)o->height || (int)r->y + (int)r->height <= (int)o->y) outside++; if ((int)r->x >= (int)n->x + (int)n->width || (int)r->x + (int)r->width <= (int)n->x || (int)r->y >= (int)n->y + (int)n->height || (int)r->y + (int)r->height <= (int)n->y) outside++; if (outside == 2) continue; } mm->redraw (mm, func); } XSetClipMask (w->gterm.display, w->gterm.gm_drawGC, None); } if (erase) XSetClipMask (w->gterm.display, w->gterm.exposeGC, None); XDestroyRegion (clip_region); XDestroyRegion (temp_region); if (func != GXxor && gm->width > 0 && gm->height > 0) { /* Redraw callback. */ gm_do_callbacks (gm, GmEvRedraw, NULL, NULL, 0); /* Generate moveResize callback, if marker was moved or resized. */ if (gm->old_rect.x != gm->cur_rect.x || gm->old_rect.y != gm->cur_rect.y || gm->old_rect.width != gm->cur_rect.width || gm->old_rect.height != gm->cur_rect.height) { char x[32], y[32]; char width[32], height[32]; char *argv[5]; int argc; /* If the marker was just created (old_rect null) or the marker * moved and we did a full erase and redraw, any old markpos is * obsolete so we may as well update the saved position. */ if (erase || !gm->old_rect.width || !gm->old_rect.height) GmMarkpos (gm); sprintf (x, "%d", gm->x); sprintf (y, "%d", gm->y); sprintf (width, "%d", gm->width); sprintf (height, "%d", gm->height); argv[0] = x; argv[1] = y; argv[2] = width; argv[3] = height; argv[4] = NULL; argc = 4; gm_do_callbacks (gm, GmEvMoveResize, NULL, argv, argc); } } } /* GmRedisplay -- Redisplay the markers in the given region, or redisplay * the entire window if the region is given as (char *)NULL. */ GmRedisplay (w, region) GtermWidget w; Region region; { register int flags = (Gm_Activated|Gm_Visible); register XRectangle *r; register Marker gm; if (!XtIsRealized ((Widget)w)) return; /* Set the clip mask to only draw in the affected region. */ if (region) XSetRegion (w->gterm.display, w->gterm.gm_drawGC, region); /* Draw all markers that intersect the target region. */ for (gm = w->gterm.gm_head; gm; gm = gm->next) { if (!((gm->flags & flags) == flags)) continue; if ((char *)region) { gm->update (gm); r = &gm->cur_rect; if (XRectInRegion (region, r->x, r->y, r->width, r->height) == RectangleOut) continue; } gm->redraw (gm, GXcopy); } XSetClipMask (w->gterm.display, w->gterm.gm_drawGC, None); w->gterm.gm_redisplay = False; } /* GmRaise -- Change the stacking order of a marker relative to another * marker, causing the first marker to be drawn above the second. */ GmRaise (gm, ref_gm) register Marker gm, ref_gm; { register GtermWidget w = gm->w; int erase; /* Already on top? */ if (gm == w->gterm.gm_tail || ref_gm && ref_gm->next == gm) return; /* Raise it. */ gm_unlink (gm); gm_linkafter (gm, ref_gm ? ref_gm : w->gterm.gm_tail); GmMarkpos (gm); GmRedraw (gm, GXcopy, erase=True); gm_refocus (w); } /* GmLower -- Change the stacking order of a marker relative to another * marker, causing the first marker to be drawn below the second. */ GmLower (gm, ref_gm) register Marker gm, ref_gm; { register GtermWidget w = gm->w; int erase; /* Already lowered? */ if (gm == w->gterm.gm_head || ref_gm && ref_gm->prev == gm) return; /* Lower it. */ gm_unlink (gm); if (ref_gm && ref_gm->prev) gm_linkafter (gm, ref_gm->prev); else { gm->next = w->gterm.gm_head; w->gterm.gm_head = gm; if (gm->next) gm->next->prev = gm; if (!w->gterm.gm_tail) w->gterm.gm_tail = gm; } GmMarkpos (gm); GmRedraw (gm, GXcopy, erase=True); gm_refocus (w); } /* GmNotify -- Notify any clients that have registered callbacks that the * given marker events have occurred. */ GmNotify (gm, events, event, params, nparams) register Marker gm; int events; XEvent *event; String *params; Cardinal nparams; { gm_do_callbacks (gm, events, event, params, nparams); } /* GmAddPt -- Add a point to a marker. */ GmAddPt (gm, x, y) register Marker gm; int x, y; { int erase; if (gm->addPt) { GmRedraw (gm, GXxor, erase=False); gm->addPt (gm, x, y); GmRedraw (gm, GXxor, erase=False); gm_refocus (gm->w); } } /* GmDeletePt -- Delete a point from a marker. */ GmDeletePt (gm, x, y) register Marker gm; int x, y; { int erase; if (gm->deletePt) { GmMarkpos (gm); gm->deletePt (gm, x, y); GmRedraw (gm, GXcopy, erase=True); gm_refocus (gm->w); } } /* GmMovePt -- Move a point within a marker. */ GmMovePt (gm, x, y) register Marker gm; int x, y; { int erase; if (gm->movePt) { GmRedraw (gm, GXxor, erase=False); gm->movePt (gm, x, y); GmRedraw (gm, GXxor, erase=False); } } /* GmMove -- Move a marker. */ GmMove (gm, x, y) register Marker gm; int x, y; { int erase; if (gm->move) { GmRedraw (gm, GXxor, erase=False); gm->move (gm, x, y); GmRedraw (gm, GXxor, erase=False); } } /* GmResize -- Resize a marker. */ GmResize (gm, x, y) register Marker gm; int x, y; { int erase; if (gm->resize) { GmRedraw (gm, GXxor, erase=False); gm->resize (gm, x, y); GmRedraw (gm, GXxor, erase=False); } } /* GmRotate -- Rotate a marker. */ GmRotate (gm, x, y) register Marker gm; int x, y; { int erase; if (gm->rotate) { GmRedraw (gm, GXxor, erase=False); gm->rotate (gm, x, y); GmRedraw (gm, GXxor, erase=False); } } /* GmSetAttributes -- Set a list of attributes. Requires that all attribute * values be specified in the same type. Autoredraw, if enabled, is suspended * until all attributes have been changed. */ GmSetAttributes (gm, args, nargs, argtype) register Marker gm; ArgList args; int nargs; char *argtype; { register int i; int autoredraw, erase; int status = OK; if (autoredraw = (gm->flags & Gm_AutoRedraw)) { gm->flags &= ~Gm_AutoRedraw; GmMarkpos (gm); } for (i=0; i < nargs; i++) { status |= GmSetAttribute (gm, args[i].name, args[i].value, argtype); if (strcmp (args[i].name, GmAutoRedraw) == 0) autoredraw = gm_getint (args[i].value, argtype); } if (autoredraw) { gm->flags |= Gm_AutoRedraw; GmRedraw (gm, GXcopy, erase=True); } return (status ? ERR : OK); } /* GmSetAttribute -- Set the value of a marker attribute. */ GmSetAttribute (gm, attribute, value, type) register Marker gm; char *attribute; XtArgVal value; char *type; { GtermWidget w = gm->w; int marker_type, atType; int erase, n, i; if (gm->flags & Gm_AutoRedraw) GmMarkpos (gm); switch (atType = gm_getattribute (attribute)) { case Ga_Type: switch (gm_gettype (type)) { case Gt_String: marker_type = GmStrToType ((char *)value); break; case Gt_Int: marker_type = gm_getint (value, type); break; default: return (ERR); } marker_type = max(1, min(Gm_NTypes, marker_type)); (gm_classinit[marker_type-1]) (gm, False); gm->flags |= Gm_Modified; break; case Ga_Activated: if (gm_getint (value, type)) { if (!(gm->flags & Gm_Activated)) { gm->flags |= Gm_Activated; GmRedraw (gm, GXcopy, erase=False); } } else { GmMarkpos (gm); gm_erase (gm); gm->flags &= ~Gm_Activated; } return (OK); case Ga_Visible: if (gm_getint (value, type)) { if (!(gm->flags & Gm_Visible)) { gm->flags |= Gm_Visible; GmRedraw (gm, GXcopy, erase=False); } } else if (gm->flags & Gm_Visible) { GmMarkpos (gm); gm_erase (gm); gm->flags &= ~Gm_Visible; } return (OK); case Ga_Sensitive: if (gm_getint (value, type)) gm->flags |= Gm_Sensitive; else gm->flags &= ~Gm_Sensitive; return (OK); case Ga_AutoRedraw: if (gm_getint (value, type)) gm->flags |= Gm_AutoRedraw; else gm->flags &= ~Gm_AutoRedraw; return (OK); case Ga_Translations: switch (gm_gettype (type)) { case Gt_String: gm->translations = XtParseTranslationTable ((char *)value); break; default: return (ERR); } return (OK); case Ga_X: gm->x = gm_getint (value, type); break; case Ga_Y: gm->y = gm_getint (value, type); break; case Ga_Width: case Ga_Height: /* For a text marker a size can be specified either in integer * pixels or in characters, e.g., "40ch" or "40 chars". */ if (gm->type == Gm_Text && type == XtRString) { XFontStruct *fp = gm->font; int char_width, char_height; int l_pix, r_pix; char *ip; for (n=0, ip=(char *)value; *ip && isdigit(*ip); ip++) n = n * 10 + (*ip - '0'); while (isspace (*ip)) ip++; if (ip[0] == 'c' && ip[1] == 'h') { char_width = fp->max_bounds.width; char_height = fp->max_bounds.ascent + fp->max_bounds.descent; l_pix = (gm->lineWidth + 1) / 2 + gm->textBorder - 1; r_pix = (gm->lineWidth + 1) / 2 + gm->textBorder - 1 - (fp->max_bounds.width - fp->max_bounds.rbearing); if (atType == Ga_Width) n = n * char_width + l_pix + r_pix; else n = n * char_height + l_pix * 2; } } else n = gm_getint (value, type); if (atType == Ga_Width) gm->width = n; else gm->height = n; break; case Ga_Rotangle: gm->rotangle = gm_getfloat (value, type); break; case Ga_HighlightColor: gm->highlightColor = gm_getpixel (w, value, type); break; case Ga_LineColor: gm->lineColor = gm_getpixel (w, value, type); break; case Ga_LineWidth: gm->lineWidth = gm_getint (value, type); break; case Ga_LineStyle: gm->lineStyle = gm_getint (value, type); break; case Ga_KnotColor: gm->knotColor = gm_getpixel (w, value, type); break; case Ga_KnotSize: gm->knotSize = gm_getint (value, type); break; case Ga_Fill: gm->fill = gm_getint (value, type); break; case Ga_FillColor: gm->fillColor = gm_getpixel (w, value, type); break; case Ga_FillBgColor: gm->fillBgColor = gm_getpixel (w, value, type); break; case Ga_FillStyle: switch (gm_gettype (type)) { case Gt_String: gm->fillStyle = gm_getfillstyle (w, value, type); break; default: break; } break; case Ga_FillPattern: switch (gm_gettype (type)) { case Gt_Pointer: gm->fillPattern = (Pixmap) (value); break; default: return (ERR); } break; case Ga_TextColor: gm->textColor = gm_getpixel (w, value, type); break; case Ga_TextBgColor: gm->textBgColor = gm_getpixel (w, value, type); break; case Ga_TextBorder: gm->textBorder = gm_getint (value, type); break; case Ga_ImageText: gm->imageText = gm_getint (value, type); break; case Ga_Font: switch (gm_gettype (type)) { case Gt_Int: i = gm_getint (value, type); if (i >= 0 && i < NDialogFonts) gm->font = w->gterm.dialog_fonts[i]; break; case Gt_Pointer: gm->font = (XFontStruct *) (value); break; default: return (ERR); } break; case Ga_Text: switch (gm_gettype (type)) { case Gt_Pointer: case Gt_String: if (gm->text) XtFree (gm->text); if (!(gm->text = XtMalloc (strlen((char *)value) + 1))) return (ERR); strcpy (gm->text, (char *)value); break; default: return (ERR); } break; default: return (ERR); } gm->flags |= Gm_Modified; if (gm->flags & Gm_AutoRedraw) GmRedraw (gm, GXcopy, erase=True); /* Notify client that a marker attribute has changed. */ { char *argv[2]; int argc; argv[0] = attribute; argv[1] = NULL; argc = 1; gm_do_callbacks (gm, GmEvModify, NULL, argv, argc); } return (OK); } /* GmGetAttributes -- Get a list of attributes. Requires that all attribute * values be specified in the same type. */ GmGetAttributes (gm, args, nargs, argtype) register Marker gm; ArgList args; int nargs; char *argtype; { register int i; for (i=0; i < nargs; i++) GmGetAttribute (gm, args[i].name, args[i].value, argtype); } /* GmGetAttribute -- Get the value of a marker attribute. */ GmGetAttribute (gm, attribute, value, type) register Marker gm; char *attribute; XtArgVal value; char *type; { GtermWidget w = gm->w; int i; switch (gm_getattribute (attribute)) { case Ga_Type: switch (gm_gettype (type)) { case Gt_String: switch (gm->type) { case Gm_Text: strcpy ((char *)value, GmText); break; case Gm_Line: strcpy ((char *)value, GmLine); break; case Gm_Polyline: strcpy ((char *)value, GmPolyline); break; case Gm_Rectangle: strcpy ((char *)value, GmRectangle); break; case Gm_Box: strcpy ((char *)value, GmBox); break; case Gm_Circle: strcpy ((char *)value, GmCircle); break; case Gm_Ellipse: strcpy ((char *)value, GmEllipse); break; case Gm_Polygon: strcpy ((char *)value, GmPolygon); break; default: return (ERR); } break; case Gt_Int: if (gm_putint (gm->type, value, type) == ERR) return (ERR); break; default: return (ERR); } break; case Ga_Activated: if (gm_putint ((gm->flags & Gm_Activated) != 0, value, type) == ERR) return (ERR); break; case Ga_Visible: if (gm_putint ((gm->flags & Gm_Visible) != 0, value, type) == ERR) return (ERR); break; case Ga_Sensitive: if (gm_putint ((gm->flags & Gm_Sensitive) != 0, value, type) == ERR) return (ERR); break; case Ga_AutoRedraw: if (gm_putint ((gm->flags & Gm_AutoRedraw) != 0, value, type) == ERR) return (ERR); break; case Ga_X: if (gm_putint (gm->x, value, type) == ERR) return (ERR); break; case Ga_Y: if (gm_putint (gm->y, value, type) == ERR) return (ERR); break; case Ga_Width: if (gm_putint (gm->width, value, type) == ERR) return (ERR); break; case Ga_Height: if (gm_putint (gm->height, value, type) == ERR) return (ERR); break; case Ga_Rotangle: if (gm_putfloat (gm->rotangle, value, type) == ERR) return (ERR); break; case Ga_HighlightColor: if (gm_putint ((int)gm->highlightColor, value, type) == ERR) return (ERR); break; case Ga_LineColor: if (gm_putint ((int)gm->lineColor, value, type) == ERR) return (ERR); break; case Ga_LineWidth: if (gm_putint (gm->lineWidth, value, type) == ERR) return (ERR); break; case Ga_LineStyle: if (gm_putint (gm->lineStyle, value, type) == ERR) return (ERR); break; case Ga_KnotColor: if (gm_putint ((int)gm->knotColor, value, type) == ERR) return (ERR); break; case Ga_KnotSize: if (gm_putint (gm->knotSize, value, type) == ERR) return (ERR); break; case Ga_Fill: if (gm_putint (gm->fill, value, type) == ERR) return (ERR); break; case Ga_FillColor: if (gm_putint ((int)gm->fillColor, value, type) == ERR) return (ERR); break; case Ga_FillBgColor: if (gm_putint ((int)gm->fillBgColor, value, type) == ERR) return (ERR); break; case Ga_FillStyle: switch (gm_gettype (type)) { case Gt_String: switch (gm->fillStyle) { case FillSolid: strcpy ((char *)value, "FillSolid"); break; case FillTiled: strcpy ((char *)value, "FillTiled"); break; case FillStippled: strcpy ((char *)value, "FillStippled"); break; case FillOpaqueStippled: strcpy ((char *)value, "FillOpaqueStippled"); break; default: strcpy ((char *)value, "FillSolid"); break; } break; case Gt_Int: if (gm_putint (gm->fillStyle, value, type) == ERR) return (ERR); break; default: return (ERR); } break; case Ga_FillPattern: switch (gm_gettype (type)) { case Gt_Pointer: *(Pixmap *)value = gm->fillPattern; break; default: return (ERR); } break; case Ga_TextColor: if (gm_putint ((int)gm->textColor, value, type) == ERR) return (ERR); break; case Ga_TextBorder: if (gm_putint (gm->textBorder, value, type) == ERR) return (ERR); break; case Ga_ImageText: if (gm_putint (gm->imageText, value, type) == ERR) return (ERR); break; case Ga_Font: switch (gm_gettype (type)) { case Gt_Int: for (i=0; i < NDialogFonts; i++) if (gm->font == w->gterm.dialog_fonts[i]) { if (gm_putint (i, value, type) == ERR) return (ERR); break; } break; case Gt_Pointer: *(XFontStruct **)value = gm->font; break; default: return (ERR); } break; case Ga_Text: switch (gm_gettype (type)) { case Gt_Pointer: *((char **)value) = gm->text; break; case Gt_String: strcpy ((char *)value, gm->text); break; default: return (ERR); } break; default: return (ERR); } return (OK); } /* GmSetVertices -- Set the vertices of a "poly" type object. */ GmSetVertices (gm, points, first, npts) Marker gm; DPoint *points; /* input array of points */ int first; /* first point to be set */ int npts; /* number of points to set */ { register DPoint *ip; register XPoint *op; register int i; int erase; /* The point vector is automatically extended if more space is needed. * Small vectors are stored directly in the marker descriptor in the * point_data array. */ if (first + npts > gm->npoints) { if (gm->npoints > GM_MAXVERTICES) { if ((gm->points = (XPoint *) XtRealloc ((char *)gm->points, first + npts)) == (XPoint *)NULL) return; } else if (first + npts > GM_MAXVERTICES) { if ((gm->points = (XPoint *) XtMalloc (first + npts)) == (XPoint *)NULL) return; } else if (!gm->points) gm->points = gm->point_data; gm->npoints = first + npts; } /* Copy the point data. */ ip = points; op = &gm->points[first]; for (i=0; i < npts; i++) { op->x = (int) ip->x + 0.5; op->y = (int) ip->y + 0.5; ip++, op++; } /* Redraw the marker if autoredraw is enabled. */ if (gm->flags & Gm_AutoRedraw) { GmMarkpos (gm); GmRedraw (gm, GXcopy, erase=True); } } /* GmGetVertices -- Get the vertices of a "poly" type object. The actual * number of points output is returned as the function value. */ GmGetVertices (gm, points, first, maxpts) register Marker gm; register DPoint *points; /* output array of points */ int first; /* first point to be returned */ int maxpts; /* max number of points to return */ { register XPoint *ip; register DPoint *op; register int i; int top, nout; if (first >= gm->npoints) return (0); top = min (first + maxpts, gm->npoints); nout = top - first; if (points) { ip = &gm->points[first]; op = points; for (i=0; i < nout; i++) { op->x = ip->x; op->y = ip->y; ip++, op++; } } return (nout); } /* GmGetBoundingBox -- Returns a rect large enough to completely enclose a * marker, regardless of its type or orientation. */ GmGetBoundingBox (gm, x, y, width, height) register Marker gm; int *x, *y; int *width, *height; { register XRectangle *r = &gm->cur_rect; *x = r->x; *y = r->y; *width = r->width; *height = r->height; } /* GmStrToType -- Convert a marker type string to a marker type code. */ GmStrToType (marker_type) register char *marker_type; { register int type; if (strcmp (marker_type, GmText) == 0) type = Gm_Text; else if (strcmp (marker_type, GmLine) == 0) type = Gm_Line; else if (strcmp (marker_type, GmPolyline) == 0) type = Gm_Polyline; else if (strcmp (marker_type, GmRectangle) == 0) type = Gm_Rectangle; else if (strcmp (marker_type, GmBox) == 0) type = Gm_Box; else if (strcmp (marker_type, GmCircle) == 0) type = Gm_Circle; else if (strcmp (marker_type, GmEllipse) == 0) type = Gm_Ellipse; else if (strcmp (marker_type, GmPolygon) == 0) type = Gm_Polygon; else type = 0; return (type); } /* GmStrToEvent -- Convert a marker event type string to a marker event code. */ GmStrToEvent (event_type) register char *event_type; { register int type; if (strcmp (event_type, "notify") == 0) type = GmEvNotify; else if (strcmp (event_type, "moveResize") == 0) type = GmEvMoveResize; else if (strcmp (event_type, "modify") == 0) type = GmEvModify; else if (strcmp (event_type, "redraw") == 0) type = GmEvRedraw; else if (strcmp (event_type, "destroy") == 0) type = GmEvDestroy ; else if (strcmp (event_type, "input") == 0) type = GmEvInput; else if (strcmp (event_type, "focusIn") == 0) type = GmEvFocusIn; else if (strcmp (event_type, "focusOut") == 0) type = GmEvFocusOut; else if (strcmp (event_type, "constraint") == 0) type = GmEvConstraint; else type = 0; return (type); } /* GmStrToFunction -- Convert a drawing function string to the corresponding * XLIB function code. */ GmStrToFunction (function) register char *function; { register int code; if (strcmp (function, "clear") == 0) code = GXclear; else if (strcmp (function, "and") == 0) code = GXand; else if (strcmp (function, "andReverse") == 0) code = GXandReverse; else if (strcmp (function, "copy") == 0) code = GXcopy; else if (strcmp (function, "andInverted") == 0) code = GXandInverted; else if (strcmp (function, "noop") == 0) code = GXnoop; else if (strcmp (function, "xor") == 0) code = GXxor; else if (strcmp (function, "or") == 0) code = GXor; else if (strcmp (function, "nor") == 0) code = GXnor; else if (strcmp (function, "equiv") == 0) code = GXequiv; else if (strcmp (function, "invert") == 0) code = GXinvert; else if (strcmp (function, "orReverse") == 0) code = GXorReverse; else if (strcmp (function, "copyInverted") == 0) code = GXcopyInverted; else if (strcmp (function, "orInverted") == 0) code = GXorInverted; else if (strcmp (function, "nand") == 0) code = GXnand; else if (strcmp (function, "set") == 0) code = GXset; else code = -1; return (code); } /* Internal procedures for above code. * ------------------------------------ */ static int gm_getint (value, type) XtArgVal value; char *type; { register int ch; switch (gm_gettype (type)) { case Gt_Bool: case Gt_Int: return ((int)value); case Gt_DFloatP: return (*(double *)value); case Gt_String: ch = *((char *)value); if (ch == 'T' || ch == 't') return (1); else if (ch == 'F' || ch == 'f') return (0); else return (atoi((char *)value)); default: return (0); } } static Pixel gm_getpixel (w, value, type) GtermWidget w; XtArgVal value; char *type; { XrmValue from, to; Pixel pixel; char *str; switch (gm_gettype (type)) { case Gt_Int: /* Pixel value (colormap index). */ return ((Pixel)value); case Gt_String: /* The pixel is expressed either as a pixel number input as a string, * or as a color name. The latter case requires a type conversion. */ str = (char *)value; if (isdigit(str[0]) && (int)strlen(str) <= 3) { int index = atoi (str); pixel = w->gterm.cmap[index]; return (pixel); } if (w->gterm.useDefaultCM || !w->gterm.haveColormap) { /* Allocate color from default colormap. */ from.size = strlen ((char *)value) + 1; from.addr = (char *)value; to.addr = (caddr_t) &pixel; to.size = sizeof(pixel); if (!XtConvertAndStore ((Widget)w, XtRString, &from, XtRPixel, &to)) pixel = w->gterm.cmap[1]; } else { /* Allocate closest match from custom colormap. This is crude, * but for the standard colors this will return an exact match. */ int index, min_dist, dist, i; XColor exact, best, *cp; pixel = w->gterm.cmap[1]; if (XLookupColor (w->gterm.display, get_colormap(w), str, &exact, &best)) { min_dist = 9999; index = 1; for (i=0; i < w->gterm.ncolors; i++) { cp = &w->gterm.color[i]; dist = abs((int)exact.red - (int)cp->red) + abs((int)exact.green - (int)cp->green) + abs((int)exact.blue - (int)cp->blue); if (dist == 0) { index = i; break; } else if (dist < min_dist) { index = i; min_dist = dist; } } pixel = w->gterm.color[index].pixel; } } return (pixel); default: return (w->gterm.cmap[1]); } } static int gm_getfillstyle (w, value, type) GtermWidget w; XtArgVal value; char *type; { switch (gm_gettype (type)) { case Gt_String: if (strcmp ((char *)value, "FillSolid") == 0) return (FillSolid); else if (strcmp ((char *)value, "FillTiled") == 0) return (FillTiled); else if (strcmp ((char *)value, "FillStippled") == 0) return (FillStippled); else if (strcmp ((char *)value, "FillOpaqueStippled") == 0) return (FillOpaqueStippled); break; default: break; } return (FillSolid); } static double gm_getfloat (value, type) XtArgVal value; char *type; { switch (gm_gettype (type)) { case Gt_Bool: case Gt_Int: return ((int)value); case Gt_DFloatP: return (*(double *)value); case Gt_String: return (atof((char *)value)); default: return (0); } } static char * gm_getstring (value, type) XtArgVal value; char *type; { if (strcmp (type, XtRString) == 0) return ((char *)value); else return (""); } static int gm_putint (ival, value, type) int ival; XtArgVal value; char *type; { switch (gm_gettype (type)) { case Gt_Bool: case Gt_Int: *(int *)value = ival; break; case Gt_DFloatP: *(double *)value = (double) ival; break; case Gt_String: sprintf ((char *)value, "%d", ival); break; default: return (ERR); } return (OK); } static int gm_putfloat (fval, value, type) double fval; XtArgVal value; char *type; { switch (gm_gettype (type)) { case Gt_Bool: case Gt_Int: *(int *)value = (int) fval; break; case Gt_DFloatP: *(double *)value = fval; break; case Gt_String: sprintf ((char *)value, "%g", fval); break; default: return (ERR); } return (OK); } static int gm_gettype (type) char *type; { if (strcmp (type, XtRBool) == 0) return (Gt_Int); else if (strcmp (type, XtRInt) == 0) return (Gt_Int); else if (strcmp (type, XtRFloat) == 0) return (Gt_DFloatP); else if (strcmp (type, XtRPointer) == 0) return (Gt_Pointer); else if (strcmp (type, XtRString) == 0) return (Gt_String); else return (ERR); } static int gm_getattribute (attribute) char *attribute; { if (strcmp (attribute, GmType) == 0) return (Ga_Type); else if (strcmp (attribute, GmActivated) == 0) return (Ga_Activated); else if (strcmp (attribute, GmVisible) == 0) return (Ga_Visible); else if (strcmp (attribute, GmSensitive) == 0) return (Ga_Sensitive); else if (strcmp (attribute, GmAutoRedraw) == 0) return (Ga_AutoRedraw); else if (strcmp (attribute, GmTranslations) == 0) return (Ga_Translations); else if (strcmp (attribute, GmX) == 0) return (Ga_X); else if (strcmp (attribute, GmY) == 0) return (Ga_Y); else if (strcmp (attribute, GmWidth) == 0) return (Ga_Width); else if (strcmp (attribute, GmHeight) == 0) return (Ga_Height); else if (strcmp (attribute, GmRotangle) == 0) return (Ga_Rotangle); else if (strcmp (attribute, GmHighlightColor) == 0) return (Ga_HighlightColor); else if (strcmp (attribute, GmLineColor) == 0) return (Ga_LineColor); else if (strcmp (attribute, GmLineWidth) == 0) return (Ga_LineWidth); else if (strcmp (attribute, GmLineStyle) == 0) return (Ga_LineStyle); else if (strcmp (attribute, GmKnotColor) == 0) return (Ga_KnotColor); else if (strcmp (attribute, GmKnotSize) == 0) return (Ga_KnotSize); else if (strcmp (attribute, GmFill) == 0) return (Ga_Fill); else if (strcmp (attribute, GmFillColor) == 0) return (Ga_FillColor); else if (strcmp (attribute, GmFillBgColor) == 0) return (Ga_FillBgColor); else if (strcmp (attribute, GmFillPattern) == 0) return (Ga_FillPattern); else if (strcmp (attribute, GmFillStyle) == 0) return (Ga_FillStyle); else if (strcmp (attribute, GmTextColor) == 0) return (Ga_TextColor); else if (strcmp (attribute, GmTextBgColor) == 0) return (Ga_TextBgColor); else if (strcmp (attribute, GmTextBorder) == 0) return (Ga_TextBorder); else if (strcmp (attribute, GmImageText) == 0) return (Ga_ImageText); else if (strcmp (attribute, GmFont) == 0) return (Ga_Font); else if (strcmp (attribute, GmText) == 0) return (Ga_Text); else return (ERR); } static void gm_linkafter (gm, prev) register Marker gm; register Marker prev; { register GtermWidget w = gm->w; gm->prev = prev; gm->next = prev ? prev->next : NULL; if (prev) prev->next = gm; if (!w->gterm.gm_tail || prev == w->gterm.gm_tail) w->gterm.gm_tail = gm; if (!w->gterm.gm_head) w->gterm.gm_head = gm; w->gterm.preserve_screen++; } static void gm_unlink (gm) register Marker gm; { register GtermWidget w = gm->w; if (gm->prev) gm->prev->next = gm->next; if (gm->next) gm->next->prev = gm->prev; if (w->gterm.gm_head == gm) w->gterm.gm_head = gm->next; if (w->gterm.gm_tail == gm) w->gterm.gm_tail = gm->prev; gm->prev = gm->next = NULL; if (!w->gterm.gm_head) w->gterm.preserve_screen = 0; } /* gm_do_callbacks -- Call any client callbacks registered for the given * event type. */ static int gm_do_callbacks (gm, events, event, params, nparams) Marker gm; register int events; XEvent *event; String *params; Cardinal nparams; { register int n; register struct markerCallback *cb; struct markerCallback callback[GM_MAXCALLBACKS]; int ncallbacks, status; /* Copy the callbacks list into local memory to ensure that it is not * changed by executing a callback. */ ncallbacks = gm->ncallbacks; memmove ((char *)callback, (char *)gm->callback, sizeof (struct markerCallback) * GM_MAXCALLBACKS); for (n = ncallbacks, cb = callback; --n >= 0; cb++) if (cb->events & events) { status = cb->func (cb->client_data, gm, events, event, params, nparams); if (status) return (status); } return (0); } /* gm_constraint -- Handle the constraint callback. This is a client * callback called when a marker position or size attribute is changed * interactively at runtime. The purpose of the callback is to allow the * client to apply any constraints, e.g. to keep the marker within a * certain area or range of sizes, to forbid rotation, and so on. */ static int gm_constraint (gm, new_gm, what) register Marker gm, new_gm; register int what; { register char *ip, *op; char argbuf[2048]; char *argv[30]; int argc = 0; /* Return immediately if there are no constraint callbacks. */ if (!gm->constraints) return; /* Prepare an argument list listing the marker attributes being changed * and their old and new values. Each attribute is passed as three * arg strings: name old-value new-value. Each argument string is * allocated a fixed amount of space of SZ_NUMBER characters. */ op = argbuf; if (what & Gb_X) { strcpy (argv[argc++]=op, "x"); op += SZ_NUMBER; sprintf (argv[argc++]=op, "%d", gm->x); op += SZ_NUMBER; sprintf (argv[argc++]=op, "%d", new_gm->x); op += SZ_NUMBER; } if (what & Gb_Y) { strcpy (argv[argc++]=op, "y"); op += SZ_NUMBER; sprintf (argv[argc++]=op, "%d", gm->y); op += SZ_NUMBER; sprintf (argv[argc++]=op, "%d", new_gm->y); op += SZ_NUMBER; } if (what & Gb_Width) { strcpy (argv[argc++]=op, "width"); op += SZ_NUMBER; sprintf (argv[argc++]=op, "%d", gm->width); op += SZ_NUMBER; sprintf (argv[argc++]=op, "%d", new_gm->width); op += SZ_NUMBER; } if (what & Gb_Height) { strcpy (argv[argc++]=op, "height"); op += SZ_NUMBER; sprintf (argv[argc++]=op, "%d", gm->height); op += SZ_NUMBER; sprintf (argv[argc++]=op, "%d", new_gm->height); op += SZ_NUMBER; } if (what & Gb_Rotangle) { strcpy (argv[argc++]=op, "rotangle"); op += SZ_NUMBER; sprintf (argv[argc++]=op, "%g", gm->rotangle); op += SZ_NUMBER; sprintf (argv[argc++]=op, "%g", new_gm->rotangle); op += SZ_NUMBER; } /* Call any constraint callbacks. The argv value strings are modified * in place. */ gm_do_callbacks (gm, GmEvConstraint, NULL, argv, argc); /* Copy the possibly edited values back into the new_gm struct. */ ip = argbuf + SZ_NUMBER * 2; if (what & Gb_X) { new_gm->x = atoi (ip); ip += SZ_NUMBER*3; } if (what & Gb_Y) { new_gm->y = atoi (ip); ip += SZ_NUMBER*3; } if (what & Gb_Width) { new_gm->width = atoi (ip); ip += SZ_NUMBER*3; } if (what & Gb_Height) { new_gm->height = atoi (ip); ip += SZ_NUMBER*3; } if (what & Gb_Rotangle) { new_gm->rotangle = atof (ip); ip += SZ_NUMBER*3; } } static void gm_erase (gm) register Marker gm; { register GtermWidget w = gm->w; register XRectangle *r = &gm->old_rect; if (!XtIsRealized ((Widget)w)) return; /* Any clipping to the marker border is set outside this routine. */ if ((gm->flags & Gm_Visible) && !NullRect(r)) XCopyArea (w->gterm.display, w->gterm.pixmap, w->gterm.window, w->gterm.exposeGC, r->x, r->y, r->width, r->height, r->x, r->y); } /* Marker actions. * ------------------------- */ /* M_create -- Create a marker. */ static void M_create (widget, event, params, nparams) Widget widget; XEvent *event; String *params; Cardinal *nparams; { register GtermWidget w = (GtermWidget)widget; register XButtonEvent *ev = (XButtonEvent *) event; register Marker gm; int interactive, type; gmSelection what; savepos (w, event); /* If the marker has already been created in interactive mode the event * merely initializes the marker, otherwise we create and initialize a * new marker. */ if (!(gm = w->gterm.gm_create)) { type = w->gterm.gm_defaultType; if (*nparams == 1) { if (!(type = GmStrToType (params[0]))) type = w->gterm.gm_defaultType; } gm = GmCreate (w, type, interactive=True); } gm->x = ev->x; gm->y = ev->y; gm->flags |= Gm_Activated; w->gterm.gm_create = NULL; what.type = (gm->type == Gm_Polygon) ? Ge_Marker : Ge_Point; what.vertex = 0; gm_focusin (w, gm, &what); } /* M_destroy -- Destroy a marker. */ static void M_destroy (widget, event, params, nparams) Widget widget; XEvent *event; String *params; Cardinal *nparams; { register GtermWidget w = (GtermWidget)widget; register XButtonEvent *ev = (XButtonEvent *) event; register Marker gm; savepos (w, event); /* Determine which marker gets this event. */ if (!(gm = w->gterm.gm_active)) if (!(gm = GmSelect (w, ev->x, ev->y, NULL))) return; GmDestroy (gm); } /* M_destroyNull -- Destroy a marker if it is null sized. */ static void M_destroyNull (widget, event, params, nparams) Widget widget; XEvent *event; String *params; Cardinal *nparams; { register GtermWidget w = (GtermWidget)widget; register XButtonEvent *ev = (XButtonEvent *) event; register Marker gm; savepos (w, event); /* Determine which marker gets this event. */ if (!(gm = w->gterm.gm_active)) if (!(gm = GmSelect (w, ev->x, ev->y, NULL))) return; if (gm && gm->width <= 2 && gm->height <= 2) GmDestroy (gm); } /* M_set -- Set a marker attribute. */ static void M_set (widget, event, params, nparams) Widget widget; XEvent *event; String *params; Cardinal *nparams; { register GtermWidget w = (GtermWidget)widget; register XButtonEvent *ev = (XButtonEvent *) event; register Marker gm; int i; savepos (w, event); if (!(gm = w->gterm.gm_active)) if (!(gm = GmSelect (w, ev->x, ev->y, NULL))) return; for (i=0; i < *nparams; i += 2) GmSetAttribute (gm, (XtArgVal)params[i], (XtArgVal)params[i+1], XtRString); } /* M_raise -- Raise a marker to the top of the display list. */ static void M_raise (widget, event, params, nparams) Widget widget; XEvent *event; String *params; Cardinal *nparams; { register GtermWidget w = (GtermWidget)widget; register XButtonEvent *ev = (XButtonEvent *) event; register Marker gm; int i; savepos (w, event); if (!(gm = w->gterm.gm_active)) if (!(gm = GmSelect (w, ev->x, ev->y, NULL))) return; GmRaise (gm, NULL); } /* M_lower -- Lower a marker to the bottom of the display list. */ static void M_lower (widget, event, params, nparams) Widget widget; XEvent *event; String *params; Cardinal *nparams; { register GtermWidget w = (GtermWidget)widget; register XButtonEvent *ev = (XButtonEvent *) event; register Marker gm; int i; savepos (w, event); if (!(gm = w->gterm.gm_active)) if (!(gm = GmSelect (w, ev->x, ev->y, NULL))) return; GmLower (gm, NULL); } /* M_notify -- Notify any clients that have registered callbacks for the * specified type of events. */ static void M_notify (widget, event, params, nparams) Widget widget; XEvent *event; String *params; Cardinal *nparams; { register GtermWidget w = (GtermWidget)widget; register XButtonEvent *ev = (XButtonEvent *) event; register Marker gm; int events, i; savepos (w, event); if (!(gm = w->gterm.gm_active)) if (!(gm = GmSelect (w, ev->x, ev->y, NULL))) return; for (i=0, events=0; i < *nparams; i++) if (strcmp (params[i], "notify") == 0) events |= GmEvNotify; else if (strcmp (params[i], "moveResize") == 0) events |= GmEvMoveResize; else if (strcmp (params[i], "modify") == 0) events |= GmEvModify; else if (strcmp (params[i], "redraw") == 0) events |= GmEvRedraw; else if (strcmp (params[i], "destroy") == 0) events |= GmEvDestroy; else if (strcmp (params[i], "input") == 0) events |= GmEvInput; else if (strcmp (params[i], "focusIn") == 0) events |= GmEvFocusIn; else if (strcmp (params[i], "focusOut") == 0) events |= GmEvFocusOut; GmNotify (gm, events, event, params + 1, *nparams - 1); } /* M_input -- Notify any clients that have registered a input callback * that a input event has occurred. */ static void M_input (widget, event, params, nparams) Widget widget; XEvent *event; String *params; Cardinal *nparams; { register GtermWidget w = (GtermWidget)widget; register XKeyEvent *ev = (XKeyEvent *) event; register Marker gm; savepos (w, event); if (!(gm = w->gterm.gm_active)) if (!(gm = GmSelect (w, ev->x, ev->y, NULL))) return; GmNotify (gm, GmEvInput, event, params, *nparams); } /* M_markpos -- Mark the current position of the marker, e.g., so that it * can later be erased. */ static void M_markpos (widget, event, params, nparams) Widget widget; XEvent *event; String *params; Cardinal *nparams; { register GtermWidget w = (GtermWidget)widget; register XButtonEvent *ev = (XButtonEvent *) event; register Marker gm; savepos (w, event); /* Determine which marker gets this event. */ if (!(gm = w->gterm.gm_active)) if (!(gm = GmSelect (w, ev->x, ev->y, NULL))) return; GmMarkpos (gm); } /* M_markposAdd -- Execute either the markpos or add action, depending upon * the pointer location. If the pointer is over an active marker at a * location where the add action can be executed this is done, otherwise the * markpos action is executed. */ static void M_markposAdd (widget, event, params, nparams) Widget widget; XEvent *event; String *params; Cardinal *nparams; { register GtermWidget w = (GtermWidget)widget; register XButtonEvent *ev = (XButtonEvent *) event; GmSelection what = &w->gterm.gm_selection; register Marker gm; savepos (w, event); /* Get marker and type of active portion of marker. */ if (!(gm = w->gterm.gm_active)) if (!(gm = GmSelect (w, ev->x, ev->y, what))) return; /* Always do a markpos whether we Add or not. */ GmMarkpos (gm); /* Add a point if possible for the given marker and pointer location. */ if (what->type == Ge_Edge && (gm->type == Gm_Polyline || gm->type == Gm_Polygon)) GmAddPt (gm, ev->x, ev->y); } /* M_redraw -- Redraw a marker. */ static void M_redraw (widget, event, params, nparams) Widget widget; XEvent *event; String *params; Cardinal *nparams; { register GtermWidget w = (GtermWidget)widget; register XButtonEvent *ev = (XButtonEvent *) event; register Marker gm; int erase; savepos (w, event); /* Determine which marker gets this event. */ if (!(gm = w->gterm.gm_active)) if (!(gm = GmSelect (w, ev->x, ev->y, NULL))) return; /* This redraw undoes the final Xor draw. */ GmRedraw (gm, GXxor, erase=False); /* Redraw the full marker. */ GmRedraw (gm, GXcopy, erase=True); } /* M_addPt -- Add a point. */ static void M_addPt (widget, event, params, nparams) Widget widget; XEvent *event; String *params; Cardinal *nparams; { register GtermWidget w = (GtermWidget)widget; register XButtonEvent *ev = (XButtonEvent *) event; GmSelection what = &w->gterm.gm_selection; register Marker gm; savepos (w, event); /* Determine which marker gets this event. */ if (!(gm = w->gterm.gm_active)) if (!(gm = GmSelect (w, ev->x, ev->y, what))) return; /* Add a point if possible for the given marker and pointer location. */ if (what->type == Ge_Edge && (gm->type == Gm_Polyline || gm->type == Gm_Polygon)) GmAddPt (gm, ev->x, ev->y); } /* M_deletePt -- Delete a point. */ static void M_deletePt (widget, event, params, nparams) Widget widget; XEvent *event; String *params; Cardinal *nparams; { register GtermWidget w = (GtermWidget)widget; register XButtonEvent *ev = (XButtonEvent *) event; GmSelection what = &w->gterm.gm_selection; register Marker gm; savepos (w, event); /* Determine which marker gets this event. */ if (!(gm = w->gterm.gm_active)) if (!(gm = GmSelect (w, ev->x, ev->y, what))) return; if (what->type == Ge_Point) GmDeletePt (gm, ev->x, ev->y); } /* M_movePt -- Move a point. */ static void M_movePt (widget, event, params, nparams) Widget widget; XEvent *event; String *params; Cardinal *nparams; { register GtermWidget w = (GtermWidget)widget; register XButtonEvent *ev = (XButtonEvent *) event; GmSelection what = &w->gterm.gm_selection; register Marker gm; savepos (w, event); /* Determine which marker gets this event. */ if (!(gm = w->gterm.gm_active)) if (!(gm = GmSelect (w, ev->x, ev->y, what))) return; /* Move a point (vertex) if supported by marker type. */ if (what->type == Ge_Point && (gm->type == Gm_Polyline || gm->type == Gm_Polygon)) GmMovePt (gm, ev->x, ev->y); } /* M_deleteDestroy -- Delete a point or destroy a marker, depending upon the * pointer position. */ static void M_deleteDestroy (widget, event, params, nparams) Widget widget; XEvent *event; String *params; Cardinal *nparams; { register GtermWidget w = (GtermWidget)widget; register XButtonEvent *ev = (XButtonEvent *) event; GmSelection what = &w->gterm.gm_selection; register Marker gm; savepos (w, event); /* Determine which marker gets this event. */ if (!(gm = w->gterm.gm_active)) if (!(gm = GmSelect (w, ev->x, ev->y, what))) return; switch (what->type) { case Ge_Point: GmDeletePt (gm, ev->x, ev->y); break; case Ge_Marker: GmDestroy (gm); break; } } /* M_move -- Move a marker. */ static void M_move (widget, event, params, nparams) Widget widget; XEvent *event; String *params; Cardinal *nparams; { register GtermWidget w = (GtermWidget)widget; register XButtonEvent *ev = (XButtonEvent *) event; register Marker gm; savepos (w, event); /* Determine which marker gets this event. */ if (!(gm = w->gterm.gm_active)) if (!(gm = GmSelect (w, ev->x, ev->y, NULL))) return; if (ev->time - gm->time > GM_UPDATE) { GmMove (gm, ev->x, ev->y); XFlush (w->gterm.display); gm->time = ev->time; } } /* M_resize -- Resize a marker. */ static void M_resize (widget, event, params, nparams) Widget widget; XEvent *event; String *params; Cardinal *nparams; { register GtermWidget w = (GtermWidget)widget; register XButtonEvent *ev = (XButtonEvent *) event; register Marker gm; savepos (w, event); /* Determine which marker gets this event. */ if (!(gm = w->gterm.gm_active)) if (!(gm = GmSelect (w, ev->x, ev->y, NULL))) return; if (ev->time - gm->time > GM_UPDATE) { GmResize (gm, ev->x, ev->y); XFlush (w->gterm.display); gm->time = ev->time; } } /* M_moveResize -- Move a point or marker, or resize a marker, depending * upon the pointer position. */ static void M_moveResize (widget, event, params, nparams) Widget widget; XEvent *event; String *params; Cardinal *nparams; { register GtermWidget w = (GtermWidget)widget; register XButtonEvent *ev = (XButtonEvent *) event; GmSelection what = &w->gterm.gm_selection; register Marker gm; savepos (w, event); /* Determine which marker gets this event. */ if (!(gm = w->gterm.gm_active)) if (!(gm = GmSelect (w, ev->x, ev->y, what))) return; if (ev->time - gm->time > GM_UPDATE) { switch (what->type) { case Ge_Marker: GmMove (gm, ev->x, ev->y); break; case Ge_Point: if (gm->type == Gm_Polygon || gm->type == Gm_Polyline) GmMovePt (gm, ev->x, ev->y); else goto resize; break; case Ge_Edge: resize: GmResize (gm, ev->x, ev->y); break; } XFlush (w->gterm.display); gm->time = ev->time; } } /* M_rotate -- Rotate a marker. */ static void M_rotate (widget, event, params, nparams) Widget widget; XEvent *event; String *params; Cardinal *nparams; { register GtermWidget w = (GtermWidget)widget; register XButtonEvent *ev = (XButtonEvent *) event; register Marker gm; savepos (w, event); /* Determine which marker gets this event. */ if (!(gm = w->gterm.gm_active)) if (!(gm = GmSelect (w, ev->x, ev->y, NULL))) return; if (ev->time - gm->time > GM_UPDATE) { GmRotate (gm, ev->x, ev->y); XFlush (w->gterm.display); gm->time = ev->time; } } /* M_rotateResize -- Rotate or resize a marker. */ static void M_rotateResize (widget, event, params, nparams) Widget widget; XEvent *event; String *params; Cardinal *nparams; { register GtermWidget w = (GtermWidget)widget; register XButtonEvent *ev = (XButtonEvent *) event; GmSelection what = &w->gterm.gm_selection; register Marker gm; savepos (w, event); /* Determine which marker gets this event. */ if (!(gm = w->gterm.gm_active)) if (!(gm = GmSelect (w, ev->x, ev->y, NULL))) return; if (ev->time - gm->time > GM_UPDATE) { switch (what->type) { case Ge_Point: GmRotate (gm, ev->x, ev->y); break; case Ge_Edge: if (gm->flags & Gm_Smooth) GmRotate (gm, ev->x, ev->y); else GmResize (gm, ev->x, ev->y); break; default: GmResize (gm, ev->x, ev->y); break; } XFlush (w->gterm.display); gm->time = ev->time; } } /* * Marker class code. * --------------------- * Each marker class implements a subset of the following procedures. The * first set of procedures are required. The second set are optional and * may be set to NULL in the marker descriptor if not implemented by the * marker class. * * gm_xxxx_init (gm, interactive) * bool = gm_xxxx_select (gm, x, y, &what) * gm_xxxx_markpos (gm) * gm_xxxx_redraw (gm, func) * gm_xxxx_update (gm) * * gm_xxxx_addPt (gm, x, y) * gm_xxxx_deletePt (gm, x, y) * gm_xxxx_movePt (gm, x, y) * gm_xxxx_move (gm, x, y) * gm_xxxx_resize (gm, x, y) * gm_xxxx_rotate (gm, x, y) * * where xxxx is the 4 character marker class name. */ /* Marker class TEXT. */ static int gm_text_select(); static void gm_text_move(), gm_text_resize(); static void gm_text_markpos(), gm_text_redraw(); static void gm_text_update(), gm_text_updatePolygon(); static void gm_text_init (gm, interactive) register Marker gm; int interactive; { register GtermWidget w = gm->w; gm->type = Gm_Text; if (!(gm->flags & Gm_Activated)) { gm->highlightColor = w->gterm.gm_highlightColor; gm->lineColor = w->gterm.gm_TextLineColor; gm->lineWidth = w->gterm.gm_lineWidth; gm->lineStyle = w->gterm.gm_lineStyle; gm->textColor = w->gterm.gm_TextColor; gm->textBgColor = w->gterm.gm_TextBgColor; gm->textBorder = w->gterm.gm_TextBorder; gm->fill = w->gterm.gm_fill; gm->fillStyle = w->gterm.gm_fillStyle; gm->fillColor = w->gterm.gm_fillColor; gm->fillBgColor = w->gterm.gm_fillBgColor; gm->fillStyle = w->gterm.gm_fillStyle; gm->font = w->gterm.gm_TextFont; gm->imageText = False; } if (gm->points && gm->npoints > GM_MAXVERTICES) XtFree ((char *)gm->points); gm->npoints = 4 + 1; gm->points = gm->point_data; gm->select = gm_text_select; gm->markpos = gm_text_markpos; gm->redraw = gm_text_redraw; gm->update = gm_text_update; gm->addPt = NULL; gm->deletePt = NULL; gm->movePt = NULL; gm->move = gm_text_move; gm->resize = gm_text_resize; gm->rotate = NULL; if (w->gterm.gm_TextString) { if (gm->text) XtFree (gm->text); gm->text = (char *) XtMalloc (strlen(w->gterm.gm_TextString)+1); strcpy (gm->text, w->gterm.gm_TextString); } else gm->text = NULL; } static int gm_text_select (gm, x, y, what) register Marker gm; int x, y; GmSelection what; { if (gm_select (gm, x, y, what)) { if (what && what->type == Ge_Edge) what->type = Ge_Marker; return (1); } else return (0); } static void gm_text_markpos (gm) register Marker gm; { gm_markpos (gm); } static void gm_text_redraw (gm, function) register Marker gm; int function; { register GtermWidget w = gm->w; int flags = (Gm_Activated|Gm_Visible); int char_width, char_height, xsize, ysize; int breakline, l_pix, r_pix, maxch, x, y; XFontStruct *fp = gm->font; char *ip, *op, *otop; char *l_ip, *l_op; char line[1024]; if (!((gm->flags & flags) == flags)) return; if (gm->width <= 0 || gm->height <= 0) return; /* In rubber-band mode just draw the outline of the text region. */ if (function == GXxor) { int save_lineWidth = gm->lineWidth; if (gm->lineWidth <= 0) gm->lineWidth = 1; gm_redraw (gm, function); gm->lineWidth = save_lineWidth; return; } /* General case. First draw the text box. */ gm_redraw (gm, function); /* Now draw the text. */ if (!gm->text) return; char_width = fp->max_bounds.width; char_height = fp->max_bounds.ascent + fp->max_bounds.descent; xsize = gm->width; ysize = gm->height; l_pix = (gm->lineWidth + 1) / 2 + gm->textBorder - 1; r_pix = (gm->lineWidth + 1) / 2 + gm->textBorder - 1 - (fp->max_bounds.width - fp->max_bounds.rbearing); if ((maxch = (xsize - l_pix - r_pix) / char_width) < 1) return; x = gm->x + (gm->lineWidth + 1) / 2 + gm->textBorder + 1; y = gm->y + (gm->lineWidth + 1) / 2 + gm->textBorder + fp->max_bounds.ascent; XSetForeground (w->gterm.display, w->gterm.gm_drawGC, gm->textColor); XSetBackground (w->gterm.display, w->gterm.gm_drawGC, gm->textBgColor); XSetFont (w->gterm.display, w->gterm.gm_drawGC, fp->fid); /* Fill lines in a multiline text box. */ l_ip = l_op = NULL; otop = line + maxch; breakline = 0; for (ip = gm->text, op=line; *ip || op > line; ) { if (! *ip) { breakline++; } else if (*ip == ' ' || *ip == '\t') { l_ip = ip; l_op = op; *op++ = ' '; ip++; } else if (*ip == '\n') { ip++; breakline++; } else *op++ = *ip++; if (breakline || op > otop) { if (op > otop) { if (l_ip && l_op) { ip = l_ip + 1; *l_op = '\0'; } else { while (op > otop) { if (ip > gm->text && isprint (*(ip-1))) --ip; --op; } *op = '\0'; } } else *op = '\0'; if (gm->imageText) { while (op < otop) *op++ = ' '; *op = '\0'; XDrawImageString (w->gterm.display, w->gterm.window, w->gterm.gm_drawGC, x, y, line, strlen(line)); } else { XDrawString (w->gterm.display, w->gterm.window, w->gterm.gm_drawGC, x, y, line, strlen(line)); } y += char_height; if (breakline) y += gm->textBorder; if (y + fp->max_bounds.descent > gm->y + ysize) break; op = line; l_ip = l_op = NULL; breakline = 0; } } } static void gm_text_update (gm) register Marker gm; { register GtermWidget w = gm->w; int flags = (Gm_Activated|Gm_Visible); if (!((gm->flags & flags) == flags)) return; if (gm->width <= 0 || gm->height <= 0) return; if (gm->flags & Gm_Modified) { gm_text_updatePolygon (gm); gm_setCurRect (gm); gm->flags &= ~Gm_Modified; } } static void gm_text_move (gm, x, y) register Marker gm; int x, y; { struct marker new_gm; new_gm.x = max (0, x - gm->width / 2); new_gm.y = max (0, y - gm->height / 2); gm_constraint (gm, &new_gm, Gb_X|Gb_Y); /* corner */ gm->x = new_gm.x; gm->y = new_gm.y; gm_text_updatePolygon (gm); gm_setCurRect (gm); } static void gm_text_resize (gm, x, y) register Marker gm; int x, y; { struct marker new_gm; new_gm.width = abs (x - gm->x); new_gm.height = abs (y - gm->y); gm_constraint (gm, &new_gm, Gb_Width|Gb_Height); gm->width = new_gm.width; gm->height = new_gm.height; gm_text_updatePolygon (gm); gm_setCurRect (gm); } static void gm_text_updatePolygon (gm) register Marker gm; { register XPoint *p = gm->points; int xsize = gm->width; int ysize = gm->height; p[0].x = gm->x; p[0].y = gm->y; p[1].x = gm->x; p[1].y = gm->y + ysize; p[2].x = gm->x + xsize; p[2].y = gm->y + ysize; p[3].x = gm->x + xsize; p[3].y = gm->y; p[4].x = gm->x; p[4].y = gm->y; } /* Marker class LINE. */ static void gm_line_init (gm, interactive) register Marker gm; int interactive; { gm->type = Gm_Line; /* stub out for now */ } /* Marker class POLYLINE. */ static void gm_plin_init (gm, interactive) register Marker gm; int interactive; { gm->type = Gm_Polyline; /* stub out for now */ } /* Marker class RECTANGLE. */ static int gm_rect_select(); static void gm_rect_move(), gm_rect_resize(), gm_rect_rotate(); static void gm_rect_update(), gm_rect_updatePolygon(); static void gm_rect_init (gm, interactive) register Marker gm; int interactive; { register GtermWidget w = gm->w; gm->type = Gm_Rectangle; if (!(gm->flags & Gm_Activated)) { gm->lineWidth = w->gterm.gm_lineWidth; gm->lineStyle = w->gterm.gm_lineStyle; gm->highlightColor = w->gterm.gm_highlightColor; gm->lineColor = w->gterm.gm_RectLineColor; gm->knotColor = w->gterm.gm_RectKnotColor; gm->knotSize = w->gterm.gm_RectKnotSize; gm->fill = w->gterm.gm_fill; gm->fillStyle = w->gterm.gm_fillStyle; gm->fillColor = w->gterm.gm_fillColor; gm->fillBgColor = w->gterm.gm_fillBgColor; } if (gm->points && gm->npoints > GM_MAXVERTICES) XtFree ((char *)gm->points); gm->points = gm->point_data; gm->npoints = 4 + 1; gm->select = gm_rect_select; gm->markpos = gm_markpos; gm->update = gm_rect_update; gm->redraw = gm_redraw; gm->addPt = NULL; gm->deletePt = NULL; gm->movePt = NULL; gm->move = gm_rect_move; gm->resize = gm_rect_resize; gm->rotate = gm_rect_rotate; } static void gm_rect_update (gm) register Marker gm; { if (gm->flags & Gm_Modified) { gm_rect_updatePolygon (gm); gm_setCurRect (gm); gm->flags &= ~Gm_Modified; } } static int gm_rect_select (gm, x, y, what) register Marker gm; int x, y; GmSelection what; { if (gm_select (gm, x, y, what)) { if (what && what->type == Ge_Edge) what->type = Ge_Marker; return (1); } else return (0); } static void gm_rect_move (gm, x, y) register Marker gm; int x, y; { struct marker new_gm; new_gm.x = x; new_gm.y = y; gm_constraint (gm, &new_gm, Gb_X|Gb_Y); gm->x = new_gm.x; gm->y = new_gm.y; gm_rect_updatePolygon (gm); gm_setCurRect (gm); } static void gm_rect_resize (gm, x, y) register Marker gm; int x, y; { double cos_rotangle = cos (-(gm->rotangle)); double sin_rotangle = sin (-(gm->rotangle)); struct marker new_gm; int rx, ry; /* Translate to the marker reference frame. */ x = x - gm->x; y = y - gm->y; /* Rotate back to the unrotated reference frame. */ rx = x * cos_rotangle - y * sin_rotangle; ry = x * sin_rotangle + y * cos_rotangle; /* Compute new width and height. */ if (rx < 0) new_gm.x = gm->x - (-rx - gm->width) / 2; else new_gm.x = gm->x + (rx - gm->width) / 2; if (ry < 0) new_gm.y = gm->y - (-ry - gm->height) / 2; else new_gm.y = gm->y + (ry - gm->height) / 2; new_gm.width = gm->width + (abs(rx) - gm->width) / 2; new_gm.height = gm->height + (abs(ry) - gm->height) / 2; gm_constraint (gm, &new_gm, Gb_X|Gb_Y|Gb_Width|Gb_Height); gm->x = new_gm.x; gm->y = new_gm.y; gm->width = new_gm.width; gm->height = new_gm.height; gm_rect_updatePolygon (gm); gm_setCurRect (gm); } static void gm_rect_rotate (gm, x, y) register Marker gm; int x, y; { double alpha, theta; struct marker new_gm; if (x == gm->x && y == gm->y) gm->rotangle = 0; else { theta = atan2 ((double)(y - gm->y), (double)(x - gm->x)); alpha = atan2 ((double)gm->height, (double)gm->width); new_gm.rotangle = gm_niceAngle (theta + alpha); gm_constraint (gm, &new_gm, Gb_Rotangle); gm->rotangle = new_gm.rotangle; gm_rect_updatePolygon (gm); gm_setCurRect (gm); } } static void gm_rect_updatePolygon (gm) Marker gm; { register x, y; register XPoint *p = gm->points; double cos_rotangle, sin_rotangle; cos_rotangle = cos (gm->rotangle); sin_rotangle = sin (gm->rotangle); x = gm->width; y = gm->height; p[0].x = x * cos_rotangle - y * sin_rotangle + gm->x + 0.5; p[0].y = x * sin_rotangle + y * cos_rotangle + gm->y + 0.5; x = -x; p[1].x = x * cos_rotangle - y * sin_rotangle + gm->x + 0.5; p[1].y = x * sin_rotangle + y * cos_rotangle + gm->y + 0.5; y = -y; p[2].x = x * cos_rotangle - y * sin_rotangle + gm->x + 0.5; p[2].y = x * sin_rotangle + y * cos_rotangle + gm->y + 0.5; x = -x; p[3].x = x * cos_rotangle - y * sin_rotangle + gm->x + 0.5; p[3].y = x * sin_rotangle + y * cos_rotangle + gm->y + 0.5; p[4] = p[0]; } /* Marker class BOX. A box marker is like a rectangle except that it is * described and resized by the center and radius (width/height), like * the other "centered" marker types (circle, ellipse, etc.). */ static int gm_boxx_select(); static void gm_boxx_move(), gm_boxx_resize(), gm_boxx_rotate(); static void gm_boxx_update(), gm_boxx_updatePolygon(); static void gm_boxx_init (gm, interactive) register Marker gm; int interactive; { register GtermWidget w = gm->w; gm->type = Gm_Box; if (!(gm->flags & Gm_Activated)) { gm->lineWidth = w->gterm.gm_lineWidth; gm->lineStyle = w->gterm.gm_lineStyle; gm->highlightColor = w->gterm.gm_highlightColor; gm->lineColor = w->gterm.gm_RectLineColor; gm->knotColor = w->gterm.gm_RectKnotColor; gm->knotSize = w->gterm.gm_RectKnotSize; gm->fill = w->gterm.gm_fill; gm->fillStyle = w->gterm.gm_fillStyle; gm->fillColor = w->gterm.gm_fillColor; gm->fillBgColor = w->gterm.gm_fillBgColor; } if (gm->points && gm->npoints > GM_MAXVERTICES) XtFree ((char *)gm->points); gm->points = gm->point_data; gm->npoints = 4 + 1; gm->select = gm_boxx_select; gm->markpos = gm_markpos; gm->update = gm_boxx_update; gm->redraw = gm_redraw; gm->addPt = NULL; gm->deletePt = NULL; gm->movePt = NULL; gm->move = gm_boxx_move; gm->resize = gm_boxx_resize; gm->rotate = gm_boxx_rotate; } static void gm_boxx_update (gm) register Marker gm; { if (gm->flags & Gm_Modified) { gm_boxx_updatePolygon (gm); gm_setCurRect (gm); gm->flags &= ~Gm_Modified; } } static int gm_boxx_select (gm, x, y, what) register Marker gm; int x, y; GmSelection what; { if (gm_select (gm, x, y, what)) { if (what && what->type == Ge_Edge) what->type = Ge_Marker; return (1); } else return (0); } static void gm_boxx_move (gm, x, y) register Marker gm; int x, y; { struct marker new_gm; new_gm.x = x; new_gm.y = y; gm_constraint (gm, &new_gm, Gb_X|Gb_Y); gm->x = new_gm.x; gm->y = new_gm.y; gm_boxx_updatePolygon (gm); gm_setCurRect (gm); } static void gm_boxx_resize (gm, x, y) register Marker gm; int x, y; { double cos_rotangle = cos (-(gm->rotangle)); double sin_rotangle = sin (-(gm->rotangle)); struct marker new_gm; int rx, ry; /* Translate to the marker reference frame. */ x = x - gm->x; y = y - gm->y; /* Rotate back to the unrotated reference frame. */ rx = x * cos_rotangle - y * sin_rotangle; ry = x * sin_rotangle + y * cos_rotangle; /* Compute new width and height. */ new_gm.width = abs(rx); new_gm.height = abs(ry); gm_constraint (gm, &new_gm, Gb_Width|Gb_Height); gm->width = new_gm.width; gm->height = new_gm.height; gm_boxx_updatePolygon (gm); gm_setCurRect (gm); } static void gm_boxx_rotate (gm, x, y) register Marker gm; int x, y; { double alpha, theta; struct marker new_gm; if (x == gm->x && y == gm->y) gm->rotangle = 0; else { theta = atan2 ((double)(y - gm->y), (double)(x - gm->x)); alpha = atan2 ((double)gm->height, (double)gm->width); new_gm.rotangle = gm_niceAngle (theta + alpha); gm_constraint (gm, &new_gm, Gb_Rotangle); gm->rotangle = new_gm.rotangle; gm_boxx_updatePolygon (gm); gm_setCurRect (gm); } } static void gm_boxx_updatePolygon (gm) Marker gm; { register x, y; register XPoint *p = gm->points; double cos_rotangle, sin_rotangle; cos_rotangle = cos (gm->rotangle); sin_rotangle = sin (gm->rotangle); x = gm->width; y = gm->height; p[0].x = x * cos_rotangle - y * sin_rotangle + gm->x + 0.5; p[0].y = x * sin_rotangle + y * cos_rotangle + gm->y + 0.5; x = -x; p[1].x = x * cos_rotangle - y * sin_rotangle + gm->x + 0.5; p[1].y = x * sin_rotangle + y * cos_rotangle + gm->y + 0.5; y = -y; p[2].x = x * cos_rotangle - y * sin_rotangle + gm->x + 0.5; p[2].y = x * sin_rotangle + y * cos_rotangle + gm->y + 0.5; x = -x; p[3].x = x * cos_rotangle - y * sin_rotangle + gm->x + 0.5; p[3].y = x * sin_rotangle + y * cos_rotangle + gm->y + 0.5; p[4] = p[0]; } /* Marker class CIRCLE. */ static int gm_circ_select(); static void gm_circ_move(), gm_circ_resize(), gm_circ_rotate(); static void gm_circ_update(), gm_circ_updatePolygon(); static void gm_circ_init (gm, interactive) register Marker gm; int interactive; { register GtermWidget w = gm->w; gm->type = Gm_Circle; if (!(gm->flags & Gm_Activated)) { gm->lineWidth = w->gterm.gm_lineWidth; gm->lineStyle = w->gterm.gm_lineStyle; gm->highlightColor = w->gterm.gm_highlightColor; gm->lineColor = w->gterm.gm_CircleLineColor; gm->knotColor = w->gterm.gm_CircleKnotColor; gm->knotSize = w->gterm.gm_CircleKnotSize; gm->fill = w->gterm.gm_fill; gm->fillStyle = w->gterm.gm_fillStyle; gm->fillColor = w->gterm.gm_fillColor; gm->fillBgColor = w->gterm.gm_fillBgColor; gm->flags |= Gm_Smooth; } gm->width = gm->height = (gm->width + gm->height) / 2.0; if (gm->points && gm->npoints > GM_MAXVERTICES) XtFree ((char *)gm->points); gm->points = gm->point_data; gm->npoints = GM_NPTSCIRCLE + 1; gm->select = gm_circ_select; gm->markpos = gm_markpos; gm->update = gm_circ_update; gm->redraw = gm_redraw; gm->addPt = NULL; gm->deletePt = NULL; gm->movePt = NULL; gm->move = gm_circ_move; gm->resize = gm_circ_resize; gm->rotate = NULL; } static void gm_circ_update (gm) register Marker gm; { if (gm->flags & Gm_Modified) { gm_circ_updatePolygon (gm); gm_setCurRect (gm); gm->flags &= ~Gm_Modified; } } static int gm_circ_select (gm, x, y, what) register Marker gm; int x, y; GmSelection what; { if (gm_select (gm, x, y, what)) { if (what && what->type == Ge_Point) what->type = Ge_Edge; return (1); } else return (0); } static void gm_circ_move (gm, x, y) register Marker gm; int x, y; { struct marker new_gm; new_gm.x = x; new_gm.y = y; gm_constraint (gm, &new_gm, Gb_X|Gb_Y); gm->x = new_gm.x; gm->y = new_gm.y; gm_circ_updatePolygon (gm); gm_setCurRect (gm); } static void gm_circ_resize (gm, x, y) register Marker gm; int x, y; { struct marker new_gm; new_gm.width = new_gm.height = sqrt ((double)(SQR(x - gm->x) + SQR(y - gm->y))); gm_constraint (gm, &new_gm, Gb_Width|Gb_Height); gm->width = gm->height = new_gm.width; gm_circ_updatePolygon (gm); gm_setCurRect (gm); } static void gm_circ_updatePolygon (gm) Marker gm; { register XPoint *p = gm->points; register int npts, i, j; double theta, x, y; npts = (gm->npoints - 1) / 4; for (i=0; i < npts; i++) { theta = PI_2 / npts * i; x = gm->width * cos(theta); y = gm->height * sin(theta); j = i; p[npts*0+j].x = x + gm->x; p[npts*0+j].y = y + gm->y; x = -x; j = npts-1 - i; p[npts*1+j].x = x + gm->x; p[npts*1+j].y = y + gm->y; y = -y; j = i; p[npts*2+j].x = x + gm->x; p[npts*2+j].y = y + gm->y; x = -x; j = npts-1 - i; p[npts*3+j].x = x + gm->x; p[npts*3+j].y = y + gm->y; } p[gm->npoints-1] = p[0]; } /* Marker class ELLIPSE. */ static int gm_elip_select(); static void gm_elip_move(), gm_elip_resize(), gm_elip_rotate(); static void gm_elip_update(), gm_elip_updatePolygon(); static void gm_elip_init (gm, interactive) register Marker gm; int interactive; { register GtermWidget w = gm->w; gm->type = Gm_Ellipse; if (!(gm->flags & Gm_Activated)) { gm->lineWidth = w->gterm.gm_lineWidth; gm->lineStyle = w->gterm.gm_lineStyle; gm->highlightColor = w->gterm.gm_highlightColor; gm->lineColor = w->gterm.gm_EllipseLineColor; gm->knotColor = w->gterm.gm_EllipseKnotColor; gm->knotSize = w->gterm.gm_EllipseKnotSize; gm->fill = w->gterm.gm_fill; gm->fillStyle = w->gterm.gm_fillStyle; gm->fillColor = w->gterm.gm_fillColor; gm->fillBgColor = w->gterm.gm_fillBgColor; gm->flags |= Gm_Smooth; } if (gm->points && gm->npoints > GM_MAXVERTICES) XtFree ((char *)gm->points); gm->points = gm->point_data; gm->npoints = GM_NPTSCIRCLE + 1; gm->select = gm_elip_select; gm->markpos = gm_markpos; gm->update = gm_elip_update; gm->redraw = gm_redraw; gm->addPt = NULL; gm->deletePt = NULL; gm->movePt = NULL; gm->move = gm_elip_move; gm->resize = gm_elip_resize; gm->rotate = gm_elip_rotate; } static void gm_elip_update (gm) register Marker gm; { if (gm->flags & Gm_Modified) { gm_elip_updatePolygon (gm); gm_setCurRect (gm); gm->flags &= ~Gm_Modified; } } static int gm_elip_select (gm, x, y, what) register Marker gm; int x, y; GmSelection what; { if (gm_select (gm, x, y, what)) { if (what && what->type == Ge_Point) what->type = Ge_Edge; return (1); } else return (0); } static void gm_elip_move (gm, x, y) register Marker gm; int x, y; { struct marker new_gm; new_gm.x = x; new_gm.y = y; gm_constraint (gm, &new_gm, Gb_X|Gb_Y); gm->x = new_gm.x; gm->y = new_gm.y; gm_elip_updatePolygon (gm); gm_setCurRect (gm); } static void gm_elip_resize (gm, x, y) register Marker gm; int x, y; { struct marker new_gm; double theta = -(gm->rotangle); int rx, ry; /* Translate to the marker reference frame. */ x = x - gm->x; y = y - gm->y; /* Rotate back to the unrotated reference frame. */ rx = x * cos(theta) - y * sin(theta); ry = x * sin(theta) + y * cos(theta); /* Compute new width and height. */ new_gm.width = abs(rx); new_gm.height = abs(ry); gm_constraint (gm, &new_gm, Gb_Width|Gb_Height); gm->width = new_gm.width; gm->height = new_gm.height; gm_elip_updatePolygon (gm); gm_setCurRect (gm); } static void gm_elip_rotate (gm, x, y) register Marker gm; int x, y; { struct marker new_gm; double theta; if (x == gm->x && y == gm->y) gm->rotangle = 0; else { theta = atan2 ((double)(y - gm->y), (double)(x - gm->x)); new_gm.rotangle = gm_niceAngle (theta); gm_constraint (gm, &new_gm, Gb_Rotangle); gm->rotangle = new_gm.rotangle; gm_elip_updatePolygon (gm); gm_setCurRect (gm); } } static void gm_elip_updatePolygon (gm) Marker gm; { register XPoint *p = gm->points; register int npts, i, j; double cos_rotangle, sin_rotangle; double theta, x, y; npts = (gm->npoints - 1) / 4; cos_rotangle = cos (gm->rotangle); sin_rotangle = sin (gm->rotangle); for (i=0; i < npts; i++) { theta = PI_2 / npts * i; x = gm->width * cos(theta); y = gm->height * sin(theta); j = i; p[npts*0+j].x = x * cos_rotangle - y * sin_rotangle + gm->x + 0.5; p[npts*0+j].y = x * sin_rotangle + y * cos_rotangle + gm->y + 0.5; x = -x; j = npts-1 - i; p[npts*1+j].x = x * cos_rotangle - y * sin_rotangle + gm->x + 0.5; p[npts*1+j].y = x * sin_rotangle + y * cos_rotangle + gm->y + 0.5; y = -y; j = i; p[npts*2+j].x = x * cos_rotangle - y * sin_rotangle + gm->x + 0.5; p[npts*2+j].y = x * sin_rotangle + y * cos_rotangle + gm->y + 0.5; x = -x; j = npts-1 - i; p[npts*3+j].x = x * cos_rotangle - y * sin_rotangle + gm->x + 0.5; p[npts*3+j].y = x * sin_rotangle + y * cos_rotangle + gm->y + 0.5; } p[gm->npoints-1] = p[0]; } /* Marker class POLYGON. */ static int gm_pgon_select(); static void gm_pgon_addPt(), gm_pgon_deletePt(), gm_pgon_movePt(); static void gm_pgon_move(), gm_pgon_resize(), gm_pgon_rotate(); static void gm_pgon_redraw(), gm_pgon_update(), gm_pgon_updatePolygon(); static void gm_pgon_init (gm, interactive) register Marker gm; int interactive; { register GtermWidget w = gm->w; register DPoint *p; gm->type = Gm_Polygon; if (!(gm->flags & Gm_Activated)) { gm->lineWidth = w->gterm.gm_lineWidth; gm->lineStyle = w->gterm.gm_lineStyle; gm->highlightColor = w->gterm.gm_highlightColor; gm->lineColor = w->gterm.gm_PgonLineColor; gm->knotColor = w->gterm.gm_PgonKnotColor; gm->knotSize = w->gterm.gm_PgonKnotSize; gm->fill = w->gterm.gm_fill; gm->fillStyle = w->gterm.gm_fillStyle; gm->fillColor = w->gterm.gm_fillColor; gm->fillBgColor = w->gterm.gm_fillBgColor; gm->npoints = gm->pgon_npts = 4 + 1; gm->points = gm->point_data; if (gm->pgon) XtFree ((char *)gm->pgon); /* Start out with a small square polygon. */ gm->pgon = p = (DPoint *) XtMalloc (5 * sizeof (DPoint)); gm->x = w->gterm.last_x; gm->y = w->gterm.last_y; if (p) { p[0].x = -1; p[0].y = -1; p[1].x = -1; p[1].y = 1; p[2].x = 1; p[2].y = 1; p[3].x = 1; p[3].y = -1; p[4].x = -1; p[4].y = -1; gm_pgon_updatePolygon (gm); gm_setCurRect (gm); } if (interactive) gm->flags |= Gm_PgonInit; } if (gm->points && gm->npoints > GM_MAXVERTICES) XtFree ((char *)gm->points); gm->points = gm->point_data; /* The following gets executed when an existing non-polygon marker is * turned into a polygon marker. */ if (gm->pgon && gm->pgon_npts) gm->npoints = gm->pgon_npts; else { gm->npoints = gm->pgon_npts = 4 + 1; /* Start out with a small square polygon. */ gm->pgon = p = (DPoint *) XtMalloc (5 * sizeof (DPoint)); if (p) { p[0].x = -gm->width; p[0].y = -gm->height; p[1].x = -gm->width; p[1].y = gm->height; p[2].x = gm->width; p[2].y = gm->height; p[3].x = gm->width; p[3].y = -gm->height; p[4].x = -gm->width; p[4].y = -gm->height; gm_pgon_updatePolygon (gm); gm_setCurRect (gm); } } gm->select = gm_select; gm->markpos = gm_markpos; gm->update = gm_pgon_update; gm->redraw = gm_pgon_redraw; gm->addPt = gm_pgon_addPt; gm->deletePt = gm_pgon_deletePt; gm->movePt = gm_pgon_movePt; gm->move = gm_pgon_move; gm->resize = gm_pgon_resize; gm->rotate = gm_pgon_rotate; } static void gm_pgon_redraw (gm, function) register Marker gm; int function; { /* The PgonInit flag is set when a polygon marker is interactively created * to cause any pointer motion event to resize the marker. The first * pointer up causes a redraw which clears the flag. */ if (function != GXxor && gm->width > 1 && gm->height > 1) gm->flags &= ~Gm_PgonInit; gm_redraw (gm, function); } static void gm_pgon_update (gm) register Marker gm; { if (gm->flags & Gm_Modified) { gm_pgon_updatePolygon (gm); gm_setCurRect (gm); gm->flags &= ~Gm_Modified; } } static void gm_pgon_addPt (gm, x, y) register Marker gm; int x, y; { register DPoint *pv; register GtermWidget w = gm->w; double cos_rotangle = cos (-(gm->rotangle)); double sin_rotangle = sin (-(gm->rotangle)); int vertex, nbytes; double rx, ry; if (gm->flags & Gm_PgonInit) { gm_pgon_resize (gm, x, y); return; } /* Translate to the marker reference frame. */ x = x - gm->x; y = y - gm->y; /* Rotate back to the unrotated reference frame. */ rx = x * cos_rotangle - y * sin_rotangle; ry = x * sin_rotangle + y * cos_rotangle; /* Add the point. */ vertex = w->gterm.gm_selection.vertex; if (vertex < 0 || vertex >= gm->npoints) return; nbytes = (gm->npoints + 1) * sizeof (DPoint); if ((pv = (DPoint *) XtRealloc ((char *)gm->pgon, nbytes)) == (DPoint *)NULL) return; gm->pgon = pv; memmove (&pv[vertex+2], &pv[vertex+1], (gm->npoints - vertex - 1) * sizeof(DPoint)); pv[vertex+1].x = rx; pv[vertex+1].y = ry; gm->npoints++; nbytes = gm->npoints * sizeof (XPoint); if (gm->npoints > GM_MAXVERTICES) { if (gm->points != gm->point_data) gm->points = (XPoint *) XtRealloc ((char *)gm->points, nbytes); else gm->points = (XPoint *) XtMalloc (nbytes); } else gm->points = gm->point_data; gm_pgon_updatePolygon (gm); gm_setCurRect (gm); } static void gm_pgon_deletePt (gm, x, y) register Marker gm; int x, y; { register DPoint *pv; register GtermWidget w = gm->w; int vertex, nbytes; if (gm->npoints <= 2) return; if (gm->flags & Gm_PgonInit) { gm_pgon_resize (gm, x, y); return; } /* Delete the point. */ vertex = w->gterm.gm_selection.vertex; if (vertex < 0 || vertex >= gm->npoints) return; pv = gm->pgon; memmove (&pv[vertex], &pv[vertex+1], (gm->npoints - vertex - 1) * sizeof(DPoint)); gm->npoints--; nbytes = gm->npoints * sizeof (DPoint); if ((pv = (DPoint *) XtRealloc ((char *)gm->pgon, nbytes)) == (DPoint *)NULL) return; gm->pgon = pv; if (gm->npoints <= GM_MAXVERTICES && gm->points != gm->point_data) XtFree ((char *)gm->points); gm->points = gm->point_data; gm_pgon_updatePolygon (gm); gm_setCurRect (gm); } static void gm_pgon_movePt (gm, x, y) register Marker gm; int x, y; { register DPoint *p; register GtermWidget w = gm->w; double cos_rotangle = cos (-(gm->rotangle)); double sin_rotangle = sin (-(gm->rotangle)); double rx, ry; int vertex; if (gm->flags & Gm_PgonInit) { gm_pgon_resize (gm, x, y); return; } /* Translate to the marker reference frame. */ x = x - gm->x; y = y - gm->y; /* Rotate back to the unrotated reference frame. */ rx = x * cos_rotangle - y * sin_rotangle; ry = x * sin_rotangle + y * cos_rotangle; /* Get vertex. */ vertex = w->gterm.gm_selection.vertex; if (vertex < 0 || vertex >= gm->npoints) return; p = &gm->pgon[vertex]; /* Edit point. */ p->x = rx; p->y = ry; gm_pgon_updatePolygon (gm); gm_setCurRect (gm); } static void gm_pgon_move (gm, x, y) register Marker gm; int x, y; { struct marker new_gm; if (gm->flags & Gm_PgonInit) { gm_pgon_resize (gm, x, y); return; } new_gm.x = x; new_gm.y = y; gm_constraint (gm, &new_gm, Gb_X|Gb_Y); gm->x = new_gm.x; gm->y = new_gm.y; gm_pgon_updatePolygon (gm); gm_setCurRect (gm); } static void gm_pgon_resize (gm, x, y) Marker gm; int x, y; { register DPoint *p, *q; GtermWidget w = gm->w; double cos_rotangle = cos (-(gm->rotangle)); double sin_rotangle = sin (-(gm->rotangle)); double theta, scale, slope, rx, ry, x1, y1, x2, y2, xi; int vertex, i; /* Translate to the marker reference frame. */ x = x - gm->x; y = y - gm->y; /* Rotate back to the unrotated reference frame. */ rx = x * cos_rotangle - y * sin_rotangle; ry = x * sin_rotangle + y * cos_rotangle; /* Get first vertex of nearest edge. */ vertex = w->gterm.gm_selection.vertex; if (vertex < 0 || vertex >= gm->npoints) return; p = &gm->pgon[vertex]; q = p + 1; /* Rotate reference frame so that intercept is at y=0. */ if (abs(rx) + abs(ry) < 1.0) scale = 1.0; else { theta = atan2 (ry, rx); cos_rotangle = cos (-theta); sin_rotangle = sin (-theta); x1 = p->x * cos_rotangle - p->y * sin_rotangle; y1 = p->x * sin_rotangle + p->y * cos_rotangle; x2 = q->x * cos_rotangle - q->y * sin_rotangle; y2 = q->x * sin_rotangle + q->y * cos_rotangle; /* Compute scale factor. */ if (y1 == y2 || x1 == x2) scale = 1.0; else { slope = (y2 - y1) / (x2 - x1); xi = x1 - y1 / slope; scale = sqrt (SQR(rx) + SQR(ry)) / xi; } } /* Rescale the polygon. */ for (i=0, p=gm->pgon; i < gm->npoints; i++, p++) { p->x *= scale; p->y *= scale; } gm_pgon_updatePolygon (gm); gm_setCurRect (gm); } static void gm_pgon_rotate (gm, x, y) register Marker gm; int x, y; { register DPoint *p; register GtermWidget w = gm->w; double cos_rotangle = cos (-(gm->rotangle)); double sin_rotangle = sin (-(gm->rotangle)); double scale, alpha, beta, rx, ry; struct marker new_gm; int vertex; if (gm->flags & Gm_PgonInit) { gm_pgon_resize (gm, x, y); return; } if (x == gm->x && y == gm->y) return; /* Translate to the marker reference frame. */ x = x - gm->x; y = y - gm->y; /* Rotate back to the unrotated reference frame. */ rx = x * cos_rotangle - y * sin_rotangle; ry = x * sin_rotangle + y * cos_rotangle; if (abs(rx) + abs(ry) < 1.0) return; vertex = w->gterm.gm_selection.vertex; if (vertex < 0 || vertex >= gm->npoints) return; p = &gm->pgon[vertex]; alpha = atan2 (p->y, p->x); beta = atan2 (ry, rx); new_gm.rotangle = gm_niceAngle (gm->rotangle + (beta - alpha)); gm_constraint (gm, &new_gm, Gb_Rotangle); gm->rotangle = new_gm.rotangle; gm_pgon_updatePolygon (gm); gm_setCurRect (gm); } static void gm_pgon_updatePolygon (gm) Marker gm; { register npts, i; register DPoint *ip = gm->pgon; register XPoint *op = gm->points; double cos_rotangle, sin_rotangle; int width, height, xp, xn, yp, yn; npts = gm->npoints; cos_rotangle = cos (gm->rotangle); sin_rotangle = sin (gm->rotangle); xp = xn = yp = yn = 0; for (i=0; i < npts; i++, ip++, op++) { /* Compute the rotated point. */ op->x = ip->x * cos_rotangle - ip->y * sin_rotangle + gm->x + 0.5; op->y = ip->x * sin_rotangle + ip->y * cos_rotangle + gm->y + 0.5; /* Compute a width/height estimate for the polygon. */ if (ip->x > xp) xp = ip->x; else if (ip->x < xn) xn = ip->x; if (ip->y > yp) yp = ip->y; else if (ip->y < yn) yn = ip->y; gm->width = (xp + -xn) / 2; gm->height = (yp + -yn) / 2; } gm->points[npts-1] = gm->points[0]; gm->pgon_npts = gm->npoints; } /* Internal procedures for above code. * ----------------------------------- */ /* gm_select -- Determine if a point is within or near a marker, and if so, * determine whether the point selects a vertex, edge, or the entire marker. */ static int gm_select (gm, x, y, what) Marker gm; register int x, y; GmSelection what; { register XPoint *p, *ptop; GtermWidget w = gm->w; int v_dist = w->gterm.gm_nearVertex; int e_dist = w->gterm.gm_nearEdge; double seglen, d1, d2, s, K, frac; int ncrossings, x0, y0; XPoint *q; int n; /* Determine if the point is near a vertex. */ for (p = gm->points, n = gm->npoints - 1; --n >= 0; p++) if (abs (x - p->x) < v_dist && abs (y - p->y) < v_dist) { if (what) { what->type = Ge_Point; what->vertex = p - gm->points; } return (1); } /* Determine if the point is near an edge. The test is based on the * observation that when a point is near a line segment, the sum of the * distances from the point to either end-point of the line segment is * nearly the same as the length of the line segment. */ p = gm->points; ptop = p + gm->npoints; x0 = p->x; y0 = p->y; d1 = sqrt ((double)(SQR(x - x0) + SQR(y - y0))); for (p++; p < ptop; p++) { seglen = sqrt ((double)(SQR(p->x - x0) + SQR(p->y - y0))); d2 = sqrt ((double)(SQR(x - p->x) + SQR(y - p->y))); if (d1 + d2 - seglen < e_dist) { if (what) { what->type = Ge_Edge; what->vertex = (p - 1) - gm->points; } return (1); } d1 = d2; x0 = p->x; y0 = p->y; } /* If the marker is one of the closed polygon types, determine if the * point is inside the marker. */ switch (gm->type) { case Gm_Line: case Gm_Polyline: return (0); } for (p = gm->points, ncrossings=0; p < ptop; p++) { /* Scan forward until we find a line segment that crosses Y. */ if (p->y > y) { for (p++; p < ptop && p->y >= y; p++) ; --p; } else if (p->y < y) { for (p++; p < ptop && p->y <= y; p++) ; --p; } /* The line segment p[0]:p[1] crosses the Y plane. If this lies * entirely to the left of the X plane we can ignore it. If any * portion of the line segment lies to the right of X we compute * the point where the line intersects the Y plane. If this point * is to the right of the X plane we have a crossing. */ q = p + 1; if (q < ptop && p->x > x || q->x > x) { if (q->y == p->y) frac = (double) 0.0; else frac = (double)(y - p->y) / (double)(q->y - p->y); if ((frac * (q->x - p->x) + p->x) >= x) ncrossings++; } } if (ncrossings & 1) { if (what) what->type = Ge_Marker; return (1); } return (0); } /* gm_markpos -- Mark the current position of a marker. */ static void gm_markpos (gm) register Marker gm; { gm->old_rect = gm->cur_rect; XUnionRegion (gm->cur_region, null_region, gm->old_region); } /* gm_redraw -- Redraw a marker expressed as a list of vertices. */ static void gm_redraw (gm, function) register Marker gm; int function; { GtermWidget w = gm->w; Display *display = w->gterm.display; Window window = w->gterm.window; int flags = (Gm_Activated|Gm_Visible); GC gc = (function == GXxor) ? w->gterm.gm_rubberGC : w->gterm.gm_drawGC; if (!XtIsRealized ((Widget)w)) return; if (!((gm->flags & flags) == flags)) return; /* Fill the polygon area if indicated. */ if (gm->fill && function != GXxor) { if (gm->fillPattern) { XSetStipple (display, gc, gm->fillPattern); XSetForeground (display, gc, gm->fillColor); XSetBackground (display, gc, gm->fillBgColor); XSetFillStyle (display, gc, gm->fillStyle); } else { XSetForeground (display, gc, gm->fillColor); XSetFillStyle (display, gc, FillSolid); } XFillPolygon (display, window, gc, gm->points, gm->npoints, Nonconvex, CoordModeOrigin); } /* Set up the drawing GC. */ if (function != GXxor) { XSetFunction (display, gc, function); XSetFillStyle (display, gc, FillSolid); XSetForeground (display, gc, (gm == w->gterm.gm_active) ? gm->highlightColor : gm->lineColor); XSetLineAttributes (display, gc, gm->lineWidth + ((gm == w->gterm.gm_active) ? w->gterm.gm_highlightWidth : 0), gm->lineStyle, CapButt, (gm->type == Gm_Polygon || gm->type == Gm_Polyline) ? JoinBevel : JoinMiter); } /* Draw the marker outline. */ if (gm->lineWidth > 0) { if (gm->type == Gm_Circle || (gm->type == Gm_Ellipse && abs(gm->rotangle) < 0.01)) { /* Special case - use X arc drawing primitive. We could use the * gm->points polygon instead, as this outline polygon is * maintained for all classes of marker. */ if (w->gterm.gm_xorFill && function == GXxor) { XFillArc (display, window, gc, gm->x - gm->width, gm->y - gm->height, gm->width * 2, gm->height * 2, 0, 360 * 64); } XDrawArc (display, window, gc, gm->x - gm->width, gm->y - gm->height, gm->width * 2, gm->height * 2, 0, 360 * 64); } else { /* Draw marker expressed as a polygon. */ if (w->gterm.gm_xorFill && function == GXxor) { XFillPolygon (display, window, gc, gm->points, gm->npoints, Convex, CoordModeOrigin); } XDrawLines (display, window, gc, gm->points, gm->npoints, CoordModeOrigin); } } /* Draw the knots if enabled. */ if (function != GXxor && gm->knotSize > 0) { int knotsize = gm->knotSize; int halfsize = gm->knotSize / 2; int i; XSetForeground (display, gc, gm->knotColor); for (i=0; i < gm->npoints; i++) { XFillRectangle (display, window, gc, gm->points[i].x - halfsize, gm->points[i].y - halfsize, gm->knotSize, gm->knotSize); } } } /* gm_setCurRect -- Compute a bounding rectangle which completely encloses * a marker (assumes that the marker is expressed as list of points). */ static void gm_setCurRect (gm) Marker gm; { int border; XDestroyRegion (gm->cur_region); gm->cur_rect = null_rect; if (gm->npoints <= 0) gm->cur_region = XCreateRegion(); else { gm->cur_region = XPolygonRegion (gm->points, gm->npoints, EvenOddRule); border = (max (gm->lineWidth, gm->knotSize) + 1) / 2; border = max (border, BORDER); XShrinkRegion (gm->cur_region, -border, -border); XClipBox (gm->cur_region, &gm->cur_rect); } } /* gm_niceAngle -- Round a rotation angle to a "nice" value. */ static double gm_niceAngle (alpha) double alpha; { double tol = 0.003; double beta; if ( abs (alpha - PI_2*0) < tol) beta = PI_2*0; else if (abs (alpha - PI_2*1) < tol) beta = PI_2*1; else if (abs (alpha - PI_2*2) < tol) beta = PI_2*2; else if (abs (alpha - PI_2*3) < tol) beta = PI_2*3; else if (abs (alpha - PI_2*4) < tol) beta = PI_2*0; else beta = alpha; return (beta); }