/* $Id: main.c,v 1.33 2006/08/22 14:33:02 djdelorie Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include "xincludes.h" #include "global.h" #include "data.h" #include "action.h" #include "crosshair.h" #include "mymem.h" #include "misc.h" #include "resource.h" #include "hid.h" #include "../hidint.h" #include "lesstif.h" #ifdef HAVE_LIBDMALLOC #include #endif RCSID ("$Id: main.c,v 1.33 2006/08/22 14:33:02 djdelorie Exp $"); #ifndef XtRDouble #define XtRDouble "Double" #endif typedef struct hid_gc_struct { HID *me_pointer; Pixel color; const char *colorname; int width; EndCapStyle cap; char xor; char erase; } hid_gc_struct; extern HID lesstif_gui; extern HID lesstif_extents; #define CRASH fprintf(stderr, "HID error: pcb called unimplemented GUI function %s\n", __FUNCTION__), abort() XtAppContext app_context; Widget appwidget; Display *display; static Window window = 0; static Cursor my_cursor = 0; /* The first is the "current" pixmap. The main_ is the real one we usually use, the mask_ are the ones for doing polygon masks. The pixmap is the saved pixels, the bitmap is for the "erase" color. We set pixmap to point to main_pixmap or mask_pixmap as needed. */ static Pixmap pixmap = 0; static Pixmap main_pixmap = 0; static Pixmap mask_pixmap = 0; static Pixmap mask_bitmap = 0; static int use_mask = 0; static int pixmap_w = 0, pixmap_h = 0; Screen *screen_s; int screen; static Colormap colormap; static GC my_gc = 0, bg_gc, clip_gc = 0, bset_gc = 0, bclear_gc = 0, mask_gc = 0; static Pixel bgcolor, offlimit_color, grid_color; static int bgred, bggreen, bgblue; /* These are for the pinout windows. */ typedef struct PinoutData { struct PinoutData *prev, *next; Widget form; Window window; int left, right, top, bottom; /* PCB extents of item */ int x, y; /* PCB coordinates of upper right corner of window */ double zoom; /* PCB units per screen pixel */ int v_width, v_height; /* pixels */ void *item; } PinoutData; /* Linked list of all pinout windows. */ static PinoutData *pinouts = 0; /* If set, we are currently updating this pinout window. */ static PinoutData *pinout = 0; static int crosshair_x = 0, crosshair_y = 0; static int in_move_event = 0; Widget work_area, messages, command, hscroll, vscroll; static Widget m_mark, m_crosshair, m_grid, m_zoom, m_mode, m_status; Widget lesstif_m_layer; Widget m_click; /* This is the size, in pixels, of the viewport. */ static int view_width, view_height; /* This is the PCB location represented by the upper left corner of the viewport. Note that PCB coordinates put 0,0 in the upper left, much like X does. */ static int view_left_x = 0, view_top_y = 0; /* Denotes PCB units per screen pixel. Larger numbers mean zooming out - the largest value means you are looking at the whole board. */ static double view_zoom = 1000; static int flip_x = 0, flip_y = 0; static int thindraw = 0; static int thindrawpoly = 0; static int autofade = 0; static int flag_thindraw (int x) { return thindraw; } static int flag_thindrawpoly (int x) { return thindrawpoly; } static int flag_flipx (int x) { return flip_x; } static int flag_flipy (int x) { return flip_y; } HID_Flag lesstif_main_flag_list[] = { {"thindraw", flag_thindraw, 0}, {"thindrawpoly", flag_thindrawpoly, 0}, {"flip_x", flag_flipx, 0}, {"flip_y", flag_flipy, 0} }; REGISTER_FLAGS (lesstif_main_flag_list) /* This is the size of the current PCB work area. */ /* Use PCB->MaxWidth, PCB->MaxHeight. */ /* static int pcb_width, pcb_height; */ static Arg args[30]; static int n; #define stdarg(t,v) XtSetArg(args[n], t, v), n++ static int use_private_colormap = 0; static int stdin_listen = 0; static char *background_image_file = 0; HID_Attribute lesstif_attribute_list[] = { {"install", "Install private colormap", HID_Boolean, 0, 0, {0, 0, 0}, 0, &use_private_colormap}, #define HA_colormap 0 {"listen", "Listen on standard input for actions", HID_Boolean, 0, 0, {0, 0, 0}, 0, &stdin_listen}, #define HA_listen 1 {"bg-image", "Background Image", HID_String, 0, 0, {0, 0, 0}, 0, &background_image_file}, #define HA_bg_image 1 }; REGISTER_ATTRIBUTES (lesstif_attribute_list) static void lesstif_use_mask (int use_it); static void zoom_to (double factor, int x, int y); static void zoom_by (double factor, int x, int y); static void pinout_callback (Widget, PinoutData *, XmDrawingAreaCallbackStruct *); static void pinout_unmap (Widget, PinoutData *, void *); static void Pan (int mode, int x, int y); /* Px converts view->pcb, Vx converts pcb->view */ static inline int Vx (int x) { int rv = (x - view_left_x) / view_zoom + 0.5; if (flip_x) rv = view_width - rv; return rv; } static inline int Vy (int y) { int rv = (y - view_top_y) / view_zoom + 0.5; if (flip_y) rv = view_height - rv; return rv; } static inline int Vz (int z) { return z / view_zoom + 0.5; } static inline int Px (int x) { if (flip_x) x = view_width - x; return x * view_zoom + view_left_x; } static inline int Py (int y) { if (flip_y) y = view_height - y; return y * view_zoom + view_top_y; } static inline int Pz (int z) { return z * view_zoom; } void lesstif_coords_to_pcb (int vx, int vy, int *px, int *py) { *px = Px (vx); *py = Py (vy); } Pixel lesstif_parse_color (char *value) { XColor color; if (XParseColor (display, colormap, value, &color)) if (XAllocColor (display, colormap, &color)) return color.pixel; return 0; } static void do_color (char *value, char *which) { XColor color; if (XParseColor (display, colormap, value, &color)) if (XAllocColor (display, colormap, &color)) { stdarg (which, color.pixel); } } /* ------------------------------------------------------------ */ static char * cur_clip () { if (TEST_FLAG (ORTHOMOVEFLAG, PCB)) return "+"; if (TEST_FLAG (ALLDIRECTIONFLAG, PCB)) return "*"; if (PCB->Clipping == 0) return "X"; if (PCB->Clipping == 1) return "_/"; return "\\_"; } /* ---------------------------------------------------------------------- */ /* Local actions. */ static int PCBChanged (int argc, char **argv, int x, int y) { if (work_area == 0) return 0; /*printf("PCB Changed! %d x %d\n", PCB->MaxWidth, PCB->MaxHeight); */ n = 0; stdarg (XmNminimum, 0); stdarg (XmNvalue, 0); stdarg (XmNsliderSize, PCB->MaxWidth ? PCB->MaxWidth : 1); stdarg (XmNmaximum, PCB->MaxWidth ? PCB->MaxWidth : 1); XtSetValues (hscroll, args, n); n = 0; stdarg (XmNminimum, 0); stdarg (XmNvalue, 0); stdarg (XmNsliderSize, PCB->MaxHeight ? PCB->MaxHeight : 1); stdarg (XmNmaximum, PCB->MaxHeight ? PCB->MaxHeight : 1); XtSetValues (vscroll, args, n); zoom_by (1000000, 0, 0); hid_action ("NetlistChanged"); hid_action ("LayersChanged"); hid_action ("RouteStylesChanged"); lesstif_sizes_reset (); lesstif_update_layer_groups (); while (pinouts) pinout_unmap (0, pinouts, 0); if (PCB->Filename) { char *cp = strrchr (PCB->Filename, '/'); n = 0; stdarg (XmNtitle, cp ? cp + 1 : PCB->Filename); XtSetValues (appwidget, args, n); } return 0; } static const char setunits_syntax[] = "SetUnits(mm|mil)"; static const char setunits_help[] = "Set the default measurement units."; /* %start-doc actions SetUnits @table @code @item mil Sets the display units to mils (1/1000 inch). @item mm Sets the display units to millimeters. @end table %end-doc */ static int SetUnits (int argc, char **argv, int x, int y) { if (argc == 0) return 0; if (strcmp (argv[0], "mil") == 0) Settings.grid_units_mm = 0; if (strcmp (argv[0], "mm") == 0) Settings.grid_units_mm = 1; lesstif_sizes_reset (); lesstif_styles_update_values (); return 0; } static const char zoom_syntax[] = "Zoom()\n" "Zoom(factor)"; static const char zoom_help[] = "Various zoom factor changes."; /* %start-doc actions Zoom Changes the zoom (magnification) of the view of the board. If no arguments are passed, the view is scaled such that the board just fits inside the visible window (i.e. ``view all''). Otherwise, @var{factor} specifies a change in zoom factor. It may be prefixed by @code{+}, @code{-}, or @code{=} to change how the zoom factor is modified. The @var{factor} is a floating point number, such as @code{1.5} or @code{0.75}. @table @code @item +@var{factor} Values greater than 1.0 cause the board to be drawn smaller; more of the board will be visible. Values between 0.0 and 1.0 cause the board to be drawn bigger; less of the board will be visible. @item -@var{factor} Values greater than 1.0 cause the board to be drawn bigger; less of the board will be visible. Values between 0.0 and 1.0 cause the board to be drawn smaller; more of the board will be visible. @item =@var{factor} The @var{factor} is an absolute zoom factor; the unit for this value is "PCB units per screen pixel". Since PCB units are 0.01 mil, a @var{factor} of 1000 means 10 mils (0.01 in) per pixel, or 100 DPI, about the actual resolution of most screens - resulting in an "actual size" board. Similarly, a @var{factor} of 100 gives you a 10x actual size. @end table Note that zoom factors of zero are silently ignored. %end-doc */ static int ZoomAction (int argc, char **argv, int x, int y) { const char *vp; double v; if (x == 0 && y == 0) { x = view_width / 2; y = view_height / 2; } else { x = Vx (x); y = Vy (y); } if (argc < 1) { zoom_to (1000000, 0, 0); return 0; } vp = argv[0]; if (*vp == '+' || *vp == '-' || *vp == '=') vp++; v = strtod (vp, 0); if (v <= 0) return 1; switch (argv[0][0]) { case '-': zoom_by (1 / v, x, y); break; default: case '+': zoom_by (v, x, y); break; case '=': zoom_to (v, x, y); break; } return 0; } static int pan_thumb_mode; static int PanAction (int argc, char **argv, int x, int y) { int mode; if (argc == 2) { pan_thumb_mode = (strcasecmp (argv[0], "thumb") == 0) ? 1 : 0; mode = atoi (argv[1]); } else { pan_thumb_mode = 0; mode = atoi (argv[0]); } Pan (mode, Vx(x), Vy(y)); } static const char thindraw_syntax[] = "ThinDraw()\n" "ThinDraw(1|0)"; static const char thindraw_help[] = "Sets the global thin-draw flag."; /* %start-doc actions ThinDraw When the thindraw flag is set, all board objects are drawn using ``thin'' lines. Traces are drawn as single lines along their centerlines, other objects are drawn as outlines. If you pass @code{0}, thin draw is disabled. If you pass @code{1}, thin draw is enabled. If you pass nothing, thin draw is toggled. %end-doc */ static int ThinDraw (int argc, char **argv, int x, int y) { PinoutData *pd; if (argc == 0) thindraw = !thindraw; else if (argv[0][0] == '0') thindraw = 0; else thindraw = 1; lesstif_invalidate_all (); for (pd = pinouts; pd; pd = pd->next) pinout_callback (0, pd, 0); return 0; } static const char thindrawpoly_syntax[] = "ThinDrawPoly()\n" "ThinDrawPoly(0|1)"; static const char thindrawpoly_help[] = "Sets the thin-draw flag for polygons."; /* %start-doc actions ThinDrawPoly When the polygon thindraw flag is set, all polygons are drawn using ``thin'' lines along their outlines. If you pass @code{0}, polygon thin draw is disabled. If you pass @code{1}, polygon thin draw is enabled. If you pass nothing, polygon thin draw is toggled. %end-doc */ static int ThinDrawPoly (int argc, char **argv, int x, int y) { PinoutData *pd; if (argc == 0) thindrawpoly = !thindrawpoly; else if (argv[0][0] == '0') thindrawpoly = 0; else thindrawpoly = 1; lesstif_invalidate_all (); for (pd = pinouts; pd; pd = pd->next) pinout_callback (0, pd, 0); return 0; } static const char swapsides_syntax[] = "SwapSides(|v|h|r)"; static const char swapsides_help[] = "Swaps the side of the board you're looking at."; /* %start-doc actions SwapSides This action changes the way you view the board. @table @code @item v Flips the board over vertically (up/down). @item h Flips the board over horizontally (left/right), like flipping pages in a book. @item r Rotates the board 180 degrees without changing sides. @end table If no argument is given, the board isn't moved but the opposite side is shown. Normally, this action changes which pads and silk layer are drawn as true silk, and which are drawn as the "invisible" layer. It also determines which solder mask you see. As a special case, if the layer group for the side you're looking at is visible and currently active, and the layer group for the opposite is not visible (i.e. disabled), then this action will also swap which layer group is visible and active, effectively swapping the ``working side'' of the board. %end-doc */ static int SwapSides (int argc, char **argv, int x, int y) { int comp_group = GetLayerGroupNumberByNumber (max_layer + COMPONENT_LAYER); int solder_group = GetLayerGroupNumberByNumber (max_layer + SOLDER_LAYER); int active_group = GetLayerGroupNumberByNumber (LayerStack[0]); int comp_showing = PCB->Data->Layer[PCB->LayerGroups.Entries[comp_group][0]].On; int solder_showing = PCB->Data->Layer[PCB->LayerGroups.Entries[solder_group][0]].On; if (argc > 0) { switch (argv[0][0]) { case 'h': case 'H': flip_x = ! flip_x; break; case 'v': case 'V': flip_y = ! flip_y; break; case 'r': case 'R': flip_x = ! flip_x; flip_y = ! flip_y; break; default: return 1; } /* SwapSides will swap this */ Settings.ShowSolderSide = (flip_x == flip_y); } Settings.ShowSolderSide = !Settings.ShowSolderSide; if (Settings.ShowSolderSide) { if (active_group == comp_group && comp_showing && !solder_showing) { ChangeGroupVisibility (PCB->LayerGroups.Entries[comp_group][0], 0, 0); ChangeGroupVisibility (PCB->LayerGroups.Entries[solder_group][0], 1, 1); } } else { if (active_group == solder_group && solder_showing && !comp_showing) { ChangeGroupVisibility (PCB->LayerGroups.Entries[solder_group][0], 0, 0); ChangeGroupVisibility (PCB->LayerGroups.Entries[comp_group][0], 1, 1); } } lesstif_invalidate_all (); return 0; } static Widget m_cmd = 0, m_cmd_label; static void command_parse (char *s) { int n = 0, ws = 1; char *cp; char **argv; for (cp = s; *cp; cp++) { if (isspace ((int) *cp)) ws = 1; else { n += ws; ws = 0; } } argv = (char **) malloc ((n + 1) * sizeof (char *)); n = 0; ws = 1; for (cp = s; *cp; cp++) { if (isspace ((int) *cp)) { ws = 1; *cp = 0; } else { if (ws) argv[n++] = cp; ws = 0; } } argv[n] = 0; lesstif_call_action (argv[0], n - 1, argv + 1); } static void command_callback (Widget w, XtPointer uptr, XmTextVerifyCallbackStruct * cbs) { char *s; switch (cbs->reason) { case XmCR_ACTIVATE: s = XmTextGetString (w); lesstif_show_crosshair (0); if (strchr (s, '(')) hid_parse_actions (s, lesstif_call_action); else command_parse (s); XtFree (s); XmTextSetString (w, ""); case XmCR_LOSING_FOCUS: XtUnmanageChild (m_cmd); XtUnmanageChild (m_cmd_label); break; } } static void command_event_handler (Widget w, XtPointer p, XEvent * e, Boolean * cont) { char buf[10]; KeySym sym; int slen; switch (e->type) { case KeyPress: slen = XLookupString ((XKeyEvent *)e, buf, sizeof (buf), &sym, NULL); switch (sym) { case XK_Escape: XtUnmanageChild (m_cmd); XtUnmanageChild (m_cmd_label); XmTextSetString (w, ""); *cont = False; break; } break; } } static const char command_syntax[] = "Command()"; static const char command_help[] = "Displays the command line input window."; /* %start-doc actions Command The command window allows the user to manually enter actions to be executed. Action syntax can be done one of two ways: @table @code @item Follow the action name by an open parenthesis, arguments separated by commas, end with a close parenthesis. Example: @code{Abc(1,2,3)} @item Separate the action name and arguments by spaces. Example: @code{Abc 1 2 3}. @end table The first option allows you to have arguments with spaces in them, but the second is more ``natural'' to type for most people. Note that action names are not case sensitive, but arguments normally are. However, most actions will check for ``keywords'' in a case insensitive way. There are three ways to finish with the command window. If you press the @code{Enter} key, the command is invoked, the window goes away, and the next time you bring up the command window it's empty. If you press the @code{Esc} key, the window goes away without invoking anything, and the next time you bring up the command window it's empty. If you change focus away from the command window (i.e. click on some other window), the command window goes away but the next time you bring it up it resumes entering the command you were entering before. %end-doc */ static int Command (int argc, char **argv, int x, int y) { XtManageChild (m_cmd_label); XtManageChild (m_cmd); XmProcessTraversal (m_cmd, XmTRAVERSE_CURRENT); return 0; } static const char benchmark_syntax[] = "Benchmark()"; static const char benchmark_help[] = "Benchmark the GUI speed."; /* %start-doc actions Benchmark This action is used to speed-test the Lesstif graphics subsystem. It redraws the current screen as many times as possible in ten seconds. It reports the amount of time needed to draw the screen once. %end-doc */ static int Benchmark (int argc, char **argv, int x, int y) { int i = 0; time_t start, end; BoxType region; Drawable save_main; save_main = main_pixmap; main_pixmap = window; region.X1 = 0; region.Y1 = 0; region.X2 = PCB->MaxWidth; region.Y2 = PCB->MaxHeight; pixmap = window; XSync (display, 0); time (&start); do { XFillRectangle (display, pixmap, bg_gc, 0, 0, view_width, view_height); hid_expose_callback (&lesstif_gui, ®ion, 0); XSync (display, 0); time (&end); i++; } while (end - start < 10); printf ("%g redraws per second\n", i / 10.0); main_pixmap = save_main; return 0; } HID_Action lesstif_main_action_list[] = { {"PCBChanged", 0, PCBChanged, pcbchanged_help, pcbchanged_syntax}, {"SetUnits", 0, SetUnits, setunits_help, setunits_syntax}, {"Zoom", 0, ZoomAction, zoom_help, zoom_syntax}, {"Pan", 0, PanAction, zoom_help, zoom_syntax}, {"Thindraw", 0, ThinDraw, thindraw_help, thindraw_syntax}, {"ThindrawPoly", 0, ThinDrawPoly, thindrawpoly_help, thindrawpoly_syntax}, {"SwapSides", 0, SwapSides, swapsides_help, swapsides_syntax}, {"Command", 0, Command, command_help, command_syntax}, {"Benchmark", 0, Benchmark, benchmark_help, benchmark_syntax} }; REGISTER_ACTIONS (lesstif_main_action_list) /* ---------------------------------------------------------------------- * redraws the background image */ static int bg_w, bg_h, bgi_w, bgi_h; static Pixel **bg = 0; static XImage *bgi = 0; static enum { PT_unknown, PT_RGB565, PT_RGB888 } pixel_type = PT_unknown; static void LoadBackgroundFile (FILE *f, char *filename) { XVisualInfo vinfot, *vinfo; Visual *vis; int c, r, b; int i, nret; int p[3], rows, cols, maxval; if (fgetc(f) != 'P') { printf("bgimage: %s signature not P6\n", filename); return; } if (fgetc(f) != '6') { printf("bgimage: %s signature not P6\n", filename); return; } for (i=0; i<3; i++) { do { b = fgetc(f); if (feof(f)) return; if (b == '#') while (!feof(f) && b != '\n') b = fgetc(f); } while (!isdigit(b)); p[i] = b - '0'; while (isdigit(b = fgetc(f))) p[i] = p[i]*10 + b - '0'; } bg_w = cols = p[0]; bg_h = rows = p[1]; maxval = p[2]; setbuf(stdout, 0); bg = (Pixel **) malloc (rows * sizeof (Pixel *)); if (!bg) { printf("Out of memory loading %s\n", filename); return; } for (i=0; i= 0) free (bg[i]); free (bg); bg = 0; return; } } vis = DefaultVisual (display, DefaultScreen(display)); vinfot.visualid = XVisualIDFromVisual(vis); vinfo = XGetVisualInfo (display, VisualIDMask, &vinfot, &nret); #if 0 /* If you want to support more visuals below, you'll probably need this. */ printf("vinfo: rm %04x gm %04x bm %04x depth %d class %d\n", vinfo->red_mask, vinfo->green_mask, vinfo->blue_mask, vinfo->depth, vinfo->class); #endif if (vinfo->class == TrueColor && vinfo->depth == 16 && vinfo->red_mask == 0xf800 && vinfo->green_mask == 0x07e0 && vinfo->blue_mask == 0x001f) pixel_type = PT_RGB565; if (vinfo->class == TrueColor && vinfo->depth == 24 && vinfo->red_mask == 0xff0000 && vinfo->green_mask == 0x00ff00 && vinfo->blue_mask == 0x0000ff) pixel_type = PT_RGB888; for (r=0; r>3)<<11 | (pg>>2)<<5 | (pb>>3); break; case PT_RGB888: bg[r][c] = (pr << 16) | (pg << 8) | (pb); break; } } } } void LoadBackgroundImage (char *filename) { FILE *f = fopen(filename, "rb"); if (!f) { if (NSTRCMP (filename, "pcb-background.ppm")) perror(filename); return; } LoadBackgroundFile (f, filename); fclose(f); } static void DrawBackgroundImage () { int x, y, w, h; double xscale, yscale; int pcbwidth = PCB->MaxWidth / view_zoom; int pcbheight = PCB->MaxHeight / view_zoom; if (!window || !bg) return; if (!bgi || view_width != bgi_w || view_height != bgi_h) { if (bgi) XDestroyImage (bgi); /* Cheat - get the image, which sets up the format too. */ bgi = XGetImage (XtDisplay(work_area), window, 0, 0, view_width, view_height, -1, ZPixmap); bgi_w = view_width; bgi_h = view_height; } w = MIN (view_width, pcbwidth); h = MIN (view_height, pcbheight); xscale = (double)bg_w / PCB->MaxWidth; yscale = (double)bg_h / PCB->MaxHeight; for (y=0; y pcb) sz = pcb; n = 0; stdarg (XmNvalue, pos); stdarg (XmNsliderSize, sz); stdarg (XmNincrement, view_zoom); stdarg (XmNpageIncrement, sz); stdarg (XmNmaximum, pcb); XtSetValues (s, args, n); } void lesstif_pan_fixup () { if (view_left_x > PCB->MaxWidth - (view_width * view_zoom)) view_left_x = PCB->MaxWidth - (view_width * view_zoom); if (view_top_y > PCB->MaxHeight - (view_height * view_zoom)) view_top_y = PCB->MaxHeight - (view_height * view_zoom); if (view_left_x < 0) view_left_x = 0; if (view_top_y < 0) view_top_y = 0; if (view_width * view_zoom > PCB->MaxWidth && view_height * view_zoom > PCB->MaxHeight) { zoom_by (1, 0, 0); return; } set_scroll (hscroll, view_left_x, view_width, PCB->MaxWidth); set_scroll (vscroll, view_top_y, view_height, PCB->MaxHeight); lesstif_invalidate_all (); } static void zoom_to (double new_zoom, int x, int y) { double max_zoom, xfrac, yfrac; int cx, cy; xfrac = (double) x / (double) view_width; yfrac = (double) y / (double) view_height; if (flip_x) xfrac = 1-xfrac; if (flip_y) yfrac = 1-yfrac; max_zoom = PCB->MaxWidth / view_width; if (max_zoom < PCB->MaxHeight / view_height) max_zoom = PCB->MaxHeight / view_height; if (new_zoom < 1) new_zoom = 1; if (new_zoom > max_zoom) new_zoom = max_zoom; cx = view_left_x + view_width * xfrac * view_zoom; cy = view_top_y + view_height * yfrac * view_zoom; if (view_zoom != new_zoom) { view_zoom = new_zoom; pixel_slop = view_zoom; view_left_x = cx - view_width * xfrac * view_zoom; view_top_y = cy - view_height * yfrac * view_zoom; } lesstif_pan_fixup (); } void zoom_by (double factor, int x, int y) { zoom_to (view_zoom * factor, x, y); } static int panning = 0; static int shift_pressed; static int ctrl_pressed; static int alt_pressed; /* X and Y are in screen coordinates. */ static void Pan (int mode, int x, int y) { static int ox, oy; static int opx, opy; panning = mode; if (pan_thumb_mode) { opx = x * PCB->MaxWidth / view_width; opy = y * PCB->MaxHeight / view_height; if (flip_x) opx = PCB->MaxWidth - opx; if (flip_y) opy = PCB->MaxHeight - opy; view_left_x = opx - view_width / 2 * view_zoom; view_top_y = opy - view_height / 2 * view_zoom; lesstif_pan_fixup (); } else if (mode == 1) { ox = x; oy = y; opx = view_left_x; opy = view_top_y; } else { if (flip_x) view_left_x = opx + (x - ox) * view_zoom; else view_left_x = opx - (x - ox) * view_zoom; if (flip_y) view_top_y = opy + (y - oy) * view_zoom; else view_top_y = opy - (y - oy) * view_zoom; lesstif_pan_fixup (); } } static void mod_changed (XKeyEvent * e, int set) { switch (XKeycodeToKeysym (display, e->keycode, 0)) { case XK_Shift_L: case XK_Shift_R: shift_pressed = set; break; case XK_Control_L: case XK_Control_R: ctrl_pressed = set; break; default: return; } in_move_event = 1; HideCrosshair (1); if (panning) Pan (2, e->x, e->y); EventMoveCrosshair (Px (e->x), Py (e->y)); AdjustAttachedObjects (); RestoreCrosshair (1); in_move_event = 0; } #define M_Release 8 #define MAX_MOUSE_BUTTON 5 Resource *mouse_actions[MAX_MOUSE_BUTTON+2][16]; static void do_mouse_action (int button, int rel_mask) { int i; int mods = (shift_pressed ? M_Shift : 0) + (ctrl_pressed ? M_Ctrl : 0) + (alt_pressed ? M_Alt : 0) + rel_mask; Resource *node = mouse_actions[button][mods]; if (!node) return; for (i = 0; i < node->c; i++) if (node->v[i].value) if (hid_parse_actions (node->v[i].value, lesstif_call_action)) return; } static void work_area_input (Widget w, XtPointer v, XEvent * e, Boolean * ctd) { Resource *r; static int pressed_button = 0; static int ignore_release = 0; show_crosshair (0); switch (e->type) { case KeyPress: mod_changed (&(e->xkey), 1); if (lesstif_key_event (&(e->xkey))) return; break; case KeyRelease: mod_changed (&(e->xkey), 0); break; case ButtonPress: if (pressed_button) return; /*printf("click %d\n", e->xbutton.button); */ if (lesstif_button_event (w, e)) { ignore_release = 1; return; } ignore_release = 0; HideCrosshair (True); pressed_button = e->xbutton.button; shift_pressed = (e->xbutton.state & ShiftMask); ctrl_pressed = (e->xbutton.state & ControlMask); alt_pressed = (e->xbutton.state & Mod1Mask); do_mouse_action(e->xbutton.button, 0); RestoreCrosshair (True); break; case ButtonRelease: if (e->xbutton.button != pressed_button) return; lesstif_button_event (w, e); HideCrosshair (True); pressed_button = 0; do_mouse_action (e->xbutton.button, M_Release); RestoreCrosshair (True); break; case MotionNotify: { Window root, child; unsigned int keys_buttons; int root_x, root_y, pos_x, pos_y; while (XCheckMaskEvent (display, PointerMotionMask, e)); XQueryPointer (display, e->xmotion.window, &root, &child, &root_x, &root_y, &pos_x, &pos_y, &keys_buttons); shift_pressed = (keys_buttons & ShiftMask); ctrl_pressed = (keys_buttons & ControlMask); /*printf("m %d %d\n", Px(e->xmotion.x), Py(e->xmotion.y)); */ in_move_event = 1; if (panning) Pan (2, pos_x, pos_y); EventMoveCrosshair (Px (pos_x), Py (pos_y)); in_move_event = 0; } break; case LeaveNotify: crosshair_x = crosshair_y = -1; CrosshairOff (1); need_idle_proc (); break; case EnterNotify: in_move_event = 1; EventMoveCrosshair (Px (e->xcrossing.x), Py (e->xcrossing.y)); in_move_event = 0; CrosshairOn (1); need_idle_proc (); break; default: printf ("work_area: unknown event %d\n", e->type); break; } } static Resource * res_wrap (char *value) { Resource *tmp; tmp = resource_create (0); resource_add_val (tmp, 0, value, 0); return tmp; } static int parse_mods (char *value) { int m = 0; /* This works because "shift" and "alt" have no 'c' in them, etc. */ if (strchr(value, 's') || strchr(value, 'S')) m |= M_Shift; if (strchr(value, 'c') || strchr(value, 'C')) m |= M_Ctrl; if (strchr(value, 'a') || strchr(value, 'A')) m |= M_Alt; if (strchr(value, 'u') || strchr(value, 'U')) m |= M_Release; return m; } void lesstif_note_mouse_resource (Resource *res) { int bi, mi; #if 0 Resource *orig_actions[MAX_MOUSE_BUTTON+2][16]; fprintf(stderr, "note mouse resource:\n"); resource_dump (res); #endif for (bi=0; bic; bi++) { int button_num; Resource *button; /* All mouse-related resources must be named. The name is the mouse button number. */ if (!res->v[bi].name) continue; else if (strcasecmp (res->v[bi].name, "left") == 0) button_num = 1; else if (strcasecmp (res->v[bi].name, "middle") == 0) button_num = 2; else if (strcasecmp (res->v[bi].name, "right") == 0) button_num = 3; else if (strcasecmp (res->v[bi].name, "up") == 0) button_num = 4; else if (strcasecmp (res->v[bi].name, "down") == 0) button_num = 5; else button_num = atoi (res->v[bi].name); if (button_num < 1 || button_num > MAX_MOUSE_BUTTON) continue; if (res->v[bi].value) mouse_actions[button_num][0] = res_wrap (res->v[bi].value); if (res->v[bi].subres) { Resource *m = res->v[bi].subres; int mods; for (mi=0; mic; mi++) { switch (resource_type (m->v[mi])) { case 1: /* subres only */ mouse_actions[button_num][0] = m->v[mi].subres; break; case 10: /* value only */ mouse_actions[button_num][0] = res_wrap (m->v[mi].value); break; case 101: /* name = subres */ mods = parse_mods (m->v[mi].name); mouse_actions[button_num][mods] = m->v[mi].subres; break; case 110: /* name = value */ mods = parse_mods (m->v[mi].name); mouse_actions[button_num][mods] = res_wrap (m->v[mi].value); break; } } } } #if 0 printf("Configured mouse actions:\n"); for (bi=1; bi<=MAX_MOUSE_BUTTON; bi++) for (mi=0; mi<16; mi++) if (mouse_actions[bi][mi]) { printf("\033[32m=== %c %c %c %c %d ===\033[0m\n", mi & M_Release ? 'R' : '-', mi & M_Alt ? 'A' : '-', mi & M_Ctrl ? 'C' : '-', mi & M_Shift ? 'S' : '-', bi); resource_dump (mouse_actions[bi][mi]); } memcpy (orig_actions, mouse_actions, sizeof(mouse_actions)); #endif for (bi=0; bi<=MAX_MOUSE_BUTTON; bi++) { int i, j; for (i=15; i>0; i--) if (!mouse_actions[bi][i]) for (j=i-1; j>=(i&M_Release?8:0); j--) if (((i & j) == j) && mouse_actions[bi][j]) { mouse_actions[bi][i] = mouse_actions[bi][j]; break; } } #if 0 printf("Active mouse actions:\n"); for (bi=1; bi<=MAX_MOUSE_BUTTON; bi++) for (mi=0; mi<16; mi++) if (mouse_actions[bi][mi]) { printf("\033[31m=== %c %c %c %c %d ===\033[0m", mi & M_Release ? 'R' : '-', mi & M_Alt ? 'A' : '-', mi & M_Ctrl ? 'C' : '-', mi & M_Shift ? 'S' : '-', bi); if (!orig_actions[bi][mi]) printf("\033[34m"); resource_dump (mouse_actions[bi][mi]); if (!orig_actions[bi][mi]) printf("\033[0m"); } #endif } void lesstif_show_crosshair (int show) { static int showing = 0; static int sx, sy; static GC xor_gc = 0; if (crosshair_x < 0 || !window) return; if (xor_gc == 0) { xor_gc = XCreateGC (display, window, 0, 0); XSetFunction (display, xor_gc, GXinvert); } if (show == showing) return; if (show) { sx = Vx (crosshair_x); sy = Vy (crosshair_y); } else need_idle_proc (); XDrawLine (display, window, xor_gc, 0, sy, view_width, sy); XDrawLine (display, window, xor_gc, sx, 0, sx, view_height); showing = show; } static void work_area_expose (Widget work_area, void *me, XmDrawingAreaCallbackStruct * cbs) { XExposeEvent *e; show_crosshair (0); e = &(cbs->event->xexpose); XSetFunction (display, my_gc, GXcopy); XCopyArea (display, main_pixmap, window, my_gc, e->x, e->y, e->width, e->height, e->x, e->y); show_crosshair (1); } static void scroll_callback (Widget scroll, int *view_dim, XmScrollBarCallbackStruct * cbs) { *view_dim = cbs->value; lesstif_invalidate_all (); } static void work_area_resize (Widget work_area, void *me, XmDrawingAreaCallbackStruct * cbs) { XColor color; Dimension width, height; show_crosshair (0); n = 0; stdarg (XtNwidth, &width); stdarg (XtNheight, &height); stdarg (XmNbackground, &bgcolor); XtGetValues (work_area, args, n); view_width = width; view_height = height; color.pixel = bgcolor; XQueryColor (display, colormap, &color); bgred = color.red; bggreen = color.green; bgblue = color.blue; if (!window) return; #if 0 if (!pixmap || view_width > pixmap_w || view_height > pixmap_h) { if (pixmap_w < view_width) #endif pixmap_w = view_width; #if 0 if (pixmap_h < view_height) #endif pixmap_h = view_height; if (main_pixmap) XFreePixmap (display, main_pixmap); main_pixmap = XCreatePixmap (display, window, pixmap_w, pixmap_h, XDefaultDepth (display, screen)); if (mask_pixmap) XFreePixmap (display, mask_pixmap); mask_pixmap = XCreatePixmap (display, window, pixmap_w, pixmap_h, XDefaultDepth (display, screen)); if (mask_bitmap) XFreePixmap (display, mask_bitmap); mask_bitmap = XCreatePixmap (display, window, pixmap_w, pixmap_h, 1); pixmap = use_mask ? main_pixmap : mask_pixmap; #if 0 } #endif zoom_by (1, 0, 0); } static void work_area_first_expose (Widget work_area, void *me, XmDrawingAreaCallbackStruct * cbs) { Dimension width, height; window = XtWindow (work_area); my_gc = XCreateGC (display, window, 0, 0); n = 0; stdarg (XtNwidth, &width); stdarg (XtNheight, &height); stdarg (XmNbackground, &bgcolor); XtGetValues (work_area, args, n); view_width = width; view_height = height; offlimit_color = lesstif_parse_color (Settings.OffLimitColor); grid_color = lesstif_parse_color (Settings.GridColor); bg_gc = XCreateGC (display, window, 0, 0); XSetForeground (display, bg_gc, bgcolor); main_pixmap = XCreatePixmap (display, window, width, height, XDefaultDepth (display, screen)); mask_pixmap = XCreatePixmap (display, window, width, height, XDefaultDepth (display, screen)); mask_bitmap = XCreatePixmap (display, window, width, height, 1); pixmap = main_pixmap; pixmap_w = width; pixmap_h = height; clip_gc = XCreateGC (display, window, 0, 0); bset_gc = XCreateGC (display, mask_bitmap, 0, 0); XSetForeground (display, bset_gc, 1); bclear_gc = XCreateGC (display, mask_bitmap, 0, 0); XSetForeground (display, bclear_gc, 0); XtRemoveCallback (work_area, XmNexposeCallback, (XtCallbackProc) work_area_first_expose, 0); XtAddCallback (work_area, XmNexposeCallback, (XtCallbackProc) work_area_expose, 0); lesstif_invalidate_all (); } static Widget make_message (char *name, Widget left, int resizeable) { Widget w, f; n = 0; if (left) { stdarg (XmNleftAttachment, XmATTACH_WIDGET); stdarg (XmNleftWidget, left); } else { stdarg (XmNleftAttachment, XmATTACH_FORM); } stdarg (XmNtopAttachment, XmATTACH_FORM); stdarg (XmNbottomAttachment, XmATTACH_FORM); stdarg (XmNshadowType, XmSHADOW_IN); stdarg (XmNshadowThickness, 1); stdarg (XmNalignment, XmALIGNMENT_CENTER); stdarg (XmNmarginWidth, 4); stdarg (XmNmarginHeight, 1); if (!resizeable) stdarg (XmNresizePolicy, XmRESIZE_GROW); f = XmCreateForm (messages, name, args, n); XtManageChild (f); n = 0; stdarg (XmNtopAttachment, XmATTACH_FORM); stdarg (XmNbottomAttachment, XmATTACH_FORM); stdarg (XmNleftAttachment, XmATTACH_FORM); stdarg (XmNrightAttachment, XmATTACH_FORM); w = XmCreateLabel (f, name, args, n); XtManageChild (w); return w; } static void lesstif_do_export (HID_Attr_Val * options) { Dimension width, height; Widget menu; Widget work_area_frame; n = 0; stdarg (XtNwidth, &width); stdarg (XtNheight, &height); XtGetValues (appwidget, args, n); if (width < 1) width = 400; if (width > XDisplayWidth (display, screen)) width = XDisplayWidth (display, screen); if (height < 1) height = 300; if (height > XDisplayHeight (display, screen)) height = XDisplayHeight (display, screen); n = 0; stdarg (XmNwidth, width); stdarg (XmNheight, height); XtSetValues (appwidget, args, n); stdarg (XmNspacing, 0); mainwind = XmCreateMainWindow (appwidget, "mainWind", args, n); XtManageChild (mainwind); n = 0; stdarg (XmNmarginWidth, 0); stdarg (XmNmarginHeight, 0); menu = lesstif_menu (mainwind, "menubar", args, n); XtManageChild (menu); n = 0; stdarg (XmNshadowType, XmSHADOW_IN); work_area_frame = XmCreateFrame (mainwind, "work_area_frame", args, n); XtManageChild (work_area_frame); n = 0; do_color (Settings.BackgroundColor, XmNbackground); work_area = XmCreateDrawingArea (work_area_frame, "work_area", args, n); XtManageChild (work_area); XtAddCallback (work_area, XmNexposeCallback, (XtCallbackProc) work_area_first_expose, 0); XtAddCallback (work_area, XmNresizeCallback, (XtCallbackProc) work_area_resize, 0); /* A regular callback won't work here, because lesstif swallows any Ctrl