#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "global.h" #include "data.h" #include "action.h" #include "crosshair.h" #include "mymem.h" #include "hid.h" #include "../hidint.h" #include "lesstif.h" #ifndef XtRDouble #define XtRDouble "Double" #endif typedef struct hid_gc_struct { HID *me_pointer; Pixel color; 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 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; } HID_Flag lesstif_main_flag_list[] = { { "thindraw", flag_thindraw, 0 }, { "thindrawpoly", flag_thindrawpoly, 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; HID_Attribute lesstif_attribute_list[] = { {"install", "Install private colormap", HID_Boolean, 0, 0, {0, 0, 0}, 0, &use_private_colormap}, #define HA_colormap 0 }; 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 *); /* Px converts view->pcb, Vx converts pcb->view */ static inline int Vx (int x) { return (x - view_left_x) / view_zoom + 0.5; } static inline int Vy (int y) { return (y - view_top_y) / view_zoom + 0.5; } static inline int Vz (int z) { return z / view_zoom + 0.5; } static inline int Px (int x) { return x * view_zoom + view_left_x; } static inline int Py (int 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 (); 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 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 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 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 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 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 && strcasecmp(argv[0], "lr") == 0) ; 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(*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(*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 (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 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 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, 0, PCBChanged }, { "SetUnits", 0, 0, SetUnits }, { "Zoom", 0, 0, ZoomAction }, { "Thindraw", 0, 0, ThinDraw }, { "ThindrawPoly", 0, 0, ThinDrawPoly }, { "SwapSides", 0, 0, SwapSides }, { "Command", 0, 0, Command }, { "Benchmark", 0, 0, Benchmark }, }; REGISTER_ACTIONS(lesstif_main_action_list) /* ---------------------------------------------------------------------- */ static HID_Attribute * lesstif_get_export_options (int *n) { return 0; } static void set_scroll (Widget s, int pos, int view, int pcb) { int sz = view * view_zoom; if (sz > 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; 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 void Pan (int mode, int x, int y) { static int ox, oy; static int opx, opy; panning = mode; if (ctrl_pressed) { opx = x * PCB->MaxWidth / view_width; opy = y * PCB->MaxHeight / view_height; 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 { view_left_x = opx - (x - ox) * view_zoom; view_top_y = opy - (y - oy) * view_zoom; lesstif_pan_fixup (); } } static int 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; } static void work_area_input (Widget w, XtPointer v, XEvent *e, Boolean *ctd) { 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); switch (e->xbutton.button) { case 1: hid_actionl("Mode", "Notify", 0); break; case 2: break; case 3: Pan(1, e->xbutton.x, e->xbutton.y); break; case 4: zoom_by(0.8, e->xbutton.x, e->xbutton.y); break; case 5: zoom_by(1.25, e->xbutton.x, e->xbutton.y); break; } RestoreCrosshair (True); break; case ButtonRelease: if (e->xbutton.button != pressed_button) return; HideCrosshair (True); pressed_button = 0; /*printf("release %d\n", e->xbutton.button);*/ switch (e->xbutton.button) { case 1: hid_actionl("Mode", "Release", 0); break; case 2: break; case 3: Pan(0, e->xbutton.x, e->xbutton.y); break; case 4: break; case 5: break; } 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; } } 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) { show_crosshair(0); XExposeEvent *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; 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); Widget menu = lesstif_menu(mainwind, "menubar", args, n); XtManageChild(menu); n = 0; stdarg(XmNshadowType, XmSHADOW_IN); Widget 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