|
|
1.1 ! root 1: #include <X/mit-copyright.h> ! 2: ! 3: /* Copyright 1985, Massachusetts Institute of Technology */ ! 4: ! 5: #ifndef lint ! 6: static char *rcsid_bitmap_c = "$Header: bitmap.c,v 10.7 86/02/01 15:18:05 tony Rel $"; ! 7: #endif ! 8: ! 9: #include <errno.h> ! 10: #include <stdio.h> ! 11: #include <X/Xlib.h> ! 12: #include <sys/types.h> ! 13: ! 14: #include "../cursors/cross.cursor" ! 15: #include "../cursors/cross_mask.cursor" ! 16: #include "../cursors/ul_angle.cursor" ! 17: #include "../cursors/ul_angle_mask.cursor" ! 18: #include "../cursors/lr_angle.cursor" ! 19: #include "../cursors/lr_angle_mask.cursor" ! 20: #include "../cursors/dot.cursor" ! 21: #include "../cursors/dot_mask.cursor" ! 22: ! 23: #define TOP_MARGIN 10 ! 24: #define LEFT_MARGIN 10 ! 25: #define BOTTOM_MARGIN 10 ! 26: #define AROUND_RASTER_MARGIN 20 ! 27: #define GRID_TO_COMMAND_MARGIN 5 ! 28: #define RIGHT_MARGIN 5 ! 29: ! 30: #define MIN_SQUARE_SIZE 8 ! 31: #define DEFAULT_SQUARE_SIZE 13 ! 32: ! 33: #define bit int ! 34: #define boolean int ! 35: #define TRUE 1 ! 36: #define FALSE 0 ! 37: #define OUT_OF_RANGE 10000 ! 38: ! 39: #define min(x,y) ((x < y) ? x : y) ! 40: #define max(x,y) ((x < y) ? y : x) ! 41: ! 42: /* error handling stuff */ ! 43: extern int errno; ! 44: extern char *sys_errlist[]; ! 45: ! 46: /* global "constants" -- set once at startup time */ ! 47: /* the first few variables are not static because they are shared ! 48: with dialog.c */ ! 49: int foreground = BlackPixel; ! 50: int background = WhitePixel; ! 51: Pixmap backmap; ! 52: Pixmap border; ! 53: int borderwidth = 3; ! 54: int invertplane = 1; ! 55: int highlightplane = 1; ! 56: int mousepix = BlackPixel; ! 57: static int squares_wide = OUT_OF_RANGE; ! 58: static int squares_high = OUT_OF_RANGE; ! 59: static short *raster; ! 60: static int raster_length; /* how many shorts in the raster[] array */ ! 61: static Window outer_window, grid_window; ! 62: static Window raster_window, raster_invert_window; ! 63: static Font font; ! 64: static FontInfo fontInfo; ! 65: static Cursor cross, upper_left, lower_right, dot; ! 66: static char *filename = NULL; /* name of input file */ ! 67: static char *backup_filename; ! 68: static char *stripped_name; ! 69: /* file name without directory path or extension */ ! 70: static char *progname; /* name this program was invoked by */ ! 71: static Pattern DottedPattern = XMakePattern ( ! 72: 1 /* pattern */, ! 73: 2 /* length */, ! 74: 1); /* multiplier */ ! 75: static Pattern InverseDottedPattern = XMakePattern ( ! 76: 2 /* pattern */, ! 77: 2 /* length */, ! 78: 1); /* multiplier */ ! 79: ! 80: /* command-button data */ ! 81: #define N_COMMANDS 12 ! 82: static struct command_data { ! 83: Window window; ! 84: char *name; ! 85: int name_length; ! 86: int x_offset; /* so text is centered within command box */ ! 87: boolean inverted; ! 88: int (*proc)(); ! 89: /* function to invoke when command button is "pressed" */ ! 90: /* actually no return value, but compiler doesn't like "void" here */ ! 91: int data; /* arbitrary instance data to call procedure back with */ ! 92: } commands [N_COMMANDS]; ! 93: ! 94: /* global variables */ ! 95: /* layout-related variables */ ! 96: static int square_size; /* length of square's side, in pixels */ ! 97: static OpaqueFrame frames[N_COMMANDS+3], outer_frame; ! 98: /* frames[0] throgh frames[N_COMMANDS-1] are the command windows; ! 99: frames[N_COMMANDS] is the raster; ! 100: frames[N_COMMANDS+1] is the inverted raster; ! 101: frames[N_COMMANDS+2] is the grid */ ! 102: ! 103: /* location of x'd-through squares, if any */ ! 104: static int x1_square_exed_through = OUT_OF_RANGE; ! 105: static int y1_square_exed_through = OUT_OF_RANGE; ! 106: static int x2_square_exed_through = OUT_OF_RANGE; ! 107: static int y2_square_exed_through = OUT_OF_RANGE; ! 108: ! 109: /* location of "plus'd through" squares, if any */ ! 110: static int x1_square_plus_through = OUT_OF_RANGE; ! 111: static int y1_square_plus_through = OUT_OF_RANGE; ! 112: static int x2_square_plus_through = OUT_OF_RANGE; ! 113: static int y2_square_plus_through = OUT_OF_RANGE; ! 114: ! 115: /* location of hot spot, if any */ ! 116: static int x_hot_spot = OUT_OF_RANGE; ! 117: static int y_hot_spot = OUT_OF_RANGE; ! 118: ! 119: static boolean changed = FALSE; ! 120: /* has user changed bitmap since starting program or last write? */ ! 121: ! 122: static enum RepaintGridType {e_AgainstBackground, e_AgainstForeground, e_Invert}; ! 123: ! 124: extern char *malloc(); ! 125: ! 126: main (argc, argv) ! 127: int argc; ! 128: char **argv; ! 129: { ! 130: SetUp (argc, argv); ! 131: while (TRUE) { ! 132: XEvent event; ! 133: XNextEvent(&event); ! 134: ProcessEvent(&event); ! 135: } ! 136: } /* end of main procedure */ ! 137: ! 138: ! 139: SetUp (argc, argv) ! 140: int argc; ! 141: char **argv; ! 142: { ! 143: char *StripName(), *BackupName(), *index(); ! 144: char *option; ! 145: FILE *file; ! 146: char *geometry = NULL, *host = NULL, *dimensions = NULL; ! 147: int i; ! 148: ! 149: progname = argv[0]; ! 150: setlinebuf (stderr); ! 151: ! 152: /* Parse command line */ ! 153: for (i = 1; i < argc; i++) { ! 154: if (argv[i][0] == '=') ! 155: geometry = argv[i]; ! 156: ! 157: else if (index (argv[i], ':') != NULL) ! 158: host = argv[i]; ! 159: ! 160: else if (filename == NULL) ! 161: filename = argv[i]; ! 162: ! 163: else ! 164: dimensions = argv[i]; ! 165: } ! 166: ! 167: if (filename == NULL) { ! 168: fprintf (stderr, "%s: no file name specified\n", progname); ! 169: exit (1); ! 170: } ! 171: ! 172: stripped_name = StripName (filename); ! 173: backup_filename = BackupName (filename); ! 174: ! 175: file = fopen (filename, "r"); ! 176: if (!file && (errno != ENOENT)) { ! 177: fprintf (stderr, "%s: could not open file '%s' for reading -- %s\n", ! 178: progname, filename, sys_errlist[errno]); ! 179: exit (1); ! 180: } ! 181: ! 182: if (file) ! 183: DimensionsFromFile(file); ! 184: else { ! 185: if (dimensions != NULL) { ! 186: if (sscanf (dimensions, "%2dx%2d", &squares_wide, &squares_high) != 2) { ! 187: fprintf (stderr, "%s: invalid dimensions '%s'\n", ! 188: progname, dimensions); ! 189: exit (1); ! 190: } ! 191: if ((squares_wide <=0) || (squares_high <=0)) { ! 192: fprintf (stderr, "%s: dimensions must be positive\n", progname); ! 193: exit (1); ! 194: } ! 195: } ! 196: else /* dimensions not supplied on command line */ ! 197: squares_wide = squares_high = 16; ! 198: } ! 199: ! 200: raster_length = ((squares_wide+15)/16)*squares_high; ! 201: raster = (short *) malloc (raster_length*sizeof(short)); ! 202: ! 203: if (file) { ! 204: InitialValuesFromFile(file); ! 205: fclose (file); ! 206: } ! 207: else { ! 208: /* set raster to all 0's (background color) */ ! 209: register int i; ! 210: for (i=0;i<raster_length;i++) ! 211: raster[i] = 0; ! 212: } ! 213: ! 214: if (!XOpenDisplay(host)) { ! 215: fprintf (stderr, "%s: could not connect to X server on %s\n", ! 216: progname, host); ! 217: exit (1); ! 218: } ! 219: ! 220: backmap = WhitePixmap; ! 221: border = BlackPixmap; ! 222: if ((option = XGetDefault(progname, "BorderWidth")) != NULL) ! 223: borderwidth = atoi(option); ! 224: if ((option = XGetDefault(progname, "BodyFont")) == NULL) ! 225: option = "vtsingle"; ! 226: font = XGetFont (option); ! 227: XQueryFont (font, &fontInfo); ! 228: if (DisplayCells() > 2) { ! 229: char *fore_color = XGetDefault(progname, "Foreground"); ! 230: char *back_color = XGetDefault(progname, "Background"); ! 231: char *high_color = XGetDefault(progname, "Highlight"); ! 232: char *brdr_color = XGetDefault(progname, "Border"); ! 233: char *mous_color = XGetDefault(progname, "Mouse"); ! 234: Color fdef, bdef, hdef; ! 235: if (fore_color && XParseColor(fore_color, &fdef) && ! 236: back_color && XParseColor(back_color, &bdef) && ! 237: (high_color == NULL || XParseColor(high_color, &hdef)) && ! 238: XGetColorCells(0, 1, high_color ? 2 : 1, &invertplane, &bdef.pixel)) { ! 239: background = bdef.pixel; ! 240: backmap = XMakeTile(background); ! 241: if (high_color) { ! 242: hdef.pixel = bdef.pixel | invertplane; ! 243: XStoreColor(&hdef); ! 244: highlightplane = 1 << (ffs(invertplane) - 1); ! 245: hdef.pixel = bdef.pixel | highlightplane; ! 246: XStoreColor(&hdef); ! 247: invertplane ^= highlightplane; ! 248: } else ! 249: highlightplane = invertplane; ! 250: XStoreColor(&bdef); ! 251: foreground = background | invertplane; ! 252: fdef.pixel = foreground; ! 253: XStoreColor(&fdef); ! 254: } ! 255: if (brdr_color && XParseColor(brdr_color, &bdef) && ! 256: XGetHardwareColor(&bdef)) ! 257: border = XMakeTile(bdef.pixel); ! 258: if (mous_color && XParseColor(mous_color, &fdef) && ! 259: XGetHardwareColor(&fdef)) ! 260: mousepix = fdef.pixel; ! 261: } ! 262: ! 263: { ! 264: int right_side_bottom, right_side_width; ! 265: int minwidth, minheight; ! 266: int default_width, default_height, default_x, default_y; ! 267: int display_width = DisplayWidth(); ! 268: int display_height = DisplayHeight(); ! 269: char default_geometry[20]; ! 270: ! 271: LayoutStage1(); ! 272: right_side_bottom = frames[N_COMMANDS+1].y + frames[N_COMMANDS+1].height ! 273: + 2 /* borders */ + AROUND_RASTER_MARGIN; ! 274: right_side_width = frames[0].width + 2 /* borders */ ! 275: + GRID_TO_COMMAND_MARGIN + RIGHT_MARGIN; ! 276: OuterWindowDims (MIN_SQUARE_SIZE, right_side_width, right_side_bottom, ! 277: &minwidth, &minheight); ! 278: OuterWindowDims (DEFAULT_SQUARE_SIZE, right_side_width, right_side_bottom, ! 279: &default_width, &default_height); ! 280: default_x = min (200, display_width - default_width - 2*borderwidth); ! 281: default_y = min (200, display_height - default_height - 2*borderwidth); ! 282: sprintf (default_geometry, "=%dx%d+%d+%d", default_width, ! 283: default_height, default_x, default_y); ! 284: outer_frame.bdrwidth = borderwidth; ! 285: outer_frame.border = border; ! 286: outer_frame.background = backmap; ! 287: outer_window = XCreate ("Bitmap Editor", progname, geometry, ! 288: default_geometry, &outer_frame, minwidth, minheight); ! 289: LayoutStage2 (); ! 290: } ! 291: ! 292: upper_left = XCreateCursor (ul_angle_width, ul_angle_height, ! 293: ul_angle_bits, ul_angle_mask_bits, ul_angle_x_hot, ul_angle_y_hot, ! 294: mousepix, background, GXcopy); ! 295: lower_right = XCreateCursor (lr_angle_width, lr_angle_height, ! 296: lr_angle_bits, lr_angle_mask_bits, ! 297: lr_angle_x_hot, lr_angle_y_hot, mousepix, background, GXcopy); ! 298: cross = XCreateCursor (cross_width, cross_height, cross_bits, ! 299: cross_mask_bits, cross_x_hot, cross_y_hot, mousepix, background, ! 300: GXcopy); ! 301: dot = XCreateCursor (dot_width, dot_height, dot_bits, ! 302: dot_mask_bits, dot_x_hot, dot_y_hot, mousepix, background, ! 303: GXcopy); ! 304: XDefineCursor (outer_window, cross); ! 305: ! 306: XExpandEvents(); /* do NOT collapse adjacent MouseMotion events */ ! 307: XSelectInput (outer_window, ExposeWindow); /* to detect size changes */ ! 308: XMapWindow (outer_window); ! 309: XMapSubwindows (outer_window); ! 310: } /* end of Set_Up procedure */ ! 311: ! 312: ! 313: /* Unfortunately, the current implementation of X (version 10) does not ! 314: handle resize event notification very well. When the outer window ! 315: is resized, X sends ExposeWindow events for each subwindow BEFORE sending ! 316: an ExposeWindow for the outer window. ! 317: ! 318: If I handled the events in the order sent, I would repaint each ! 319: subwindow, only then to discover that the outer window had changed ! 320: size...at which time I would unmap and rearrange the subwindows, ! 321: causing ANOTHER set of exposure events on the subwindows. This would ! 322: not just be inefficient, it would also look ugly on the screen. ! 323: ! 324: To work around this misfeature, I do not process ExposeWindow events ! 325: immediately upon receipt, but instead go into a recursion (HandleExposure) ! 326: until I either run out of events or get a non-ExposeWindow event. If ! 327: the list of ExposeWindow events ends with a resize, I discard all of the ! 328: earlier ExposeWindow events. Otherwise, I unwind out of the recursion, ! 329: processing each exposure event in the opposite of the order received, ! 330: eventually returning to the normal event notification loop. ! 331: ! 332: This code is admittedly convoluted, and will hopefully go away in a ! 333: future version of X. */ ! 334: ! 335: #define PROCESS_IT 0 ! 336: #define DISCARD_IT 1 ! 337: ! 338: ProcessEvent (event) ! 339: register XEvent *event; ! 340: { ! 341: register Window w = event->window; ! 342: register int i; ! 343: if (event->type == ExposeWindow) { ! 344: int status; ! 345: /* make sure that I get all the exposure events that have been sent */ ! 346: XSync(0); ! 347: status = HandleExposure (event); ! 348: if (status == DISCARD_IT) ! 349: return; ! 350: } ! 351: ProcessEventReally (event); ! 352: } ! 353: ! 354: ProcessEventReally (event) ! 355: register XEvent *event; ! 356: { ! 357: register Window w = event->window; ! 358: register int i; ! 359: if (w == grid_window) ! 360: ProcessGridWindowEvent (event); ! 361: else if (w == outer_window) ! 362: ProcessOuterWindowEvent (event); ! 363: else if (w == raster_window) ! 364: RepaintRaster(); ! 365: else if (w == raster_invert_window) ! 366: RepaintRasterInverted(); ! 367: else for (i=0;i<N_COMMANDS;i++) ! 368: if (w == commands[i].window) ! 369: ProcessCommandButtonEvent (&commands[i], event); ! 370: } /* end of ProcessInput procedure */ ! 371: ! 372: ! 373: int HandleExposure (event) ! 374: XExposeWindowEvent *event; ! 375: { ! 376: int status; ! 377: XEvent next_event; ! 378: ! 379: if (QLength() == 0) ! 380: /* there are no more events, so this can't be a resize */ ! 381: return (PROCESS_IT); ! 382: ! 383: XNextEvent (&next_event); ! 384: if (next_event.type != ExposeWindow) { ! 385: /* the list of exposures ended, so this can't be a resize */ ! 386: XPutBackEvent (&next_event); ! 387: return (PROCESS_IT); ! 388: } ! 389: ! 390: if (next_event.window == outer_window) { ! 391: if ((outer_frame.height == ((XExposeWindowEvent *)&next_event)->height) ! 392: && (outer_frame.width == ((XExposeWindowEvent *)&next_event)->width)) ! 393: /* the list of exposures ended with a non-resize */ ! 394: status = PROCESS_IT; ! 395: else ! 396: /* this IS a resize */ ! 397: status = DISCARD_IT; ! 398: /* Handle the outer window exposure, whether or not it's a resize */ ! 399: ProcessEventReally (&next_event); ! 400: return (status); ! 401: } ! 402: ! 403: if ((status = HandleExposure (&next_event)) == PROCESS_IT) ! 404: ProcessEventReally (&next_event); ! 405: return (status); ! 406: } ! 407: ! 408: ProcessGridWindowEvent (event) ! 409: XEvent *event; ! 410: { ! 411: int x_square, y_square; ! 412: static int x_square_prev, y_square_prev; ! 413: static boolean raster_outdated; ! 414: switch (event->type) { ! 415: ! 416: case ExposeWindow: ! 417: RepaintGridLines(e_AgainstBackground); ! 418: RefillGrid(FALSE); ! 419: if (x1_square_exed_through != OUT_OF_RANGE) ! 420: ExThroughRectangle (x1_square_exed_through, y1_square_exed_through, ! 421: x2_square_exed_through, y2_square_exed_through); ! 422: if (x1_square_plus_through != OUT_OF_RANGE) ! 423: PlusThroughRectangle (x1_square_plus_through, y1_square_plus_through, ! 424: x2_square_plus_through, y2_square_plus_through); ! 425: if (x_hot_spot != OUT_OF_RANGE) ! 426: HighlightHotSpot(); ! 427: break; ! 428: ! 429: case ExposeRegion: { ! 430: #define this_event ((XExposeRegionEvent *)event) ! 431: int x1 = this_event->x; ! 432: int y1 = this_event->y; ! 433: int x2 = x1 + this_event->width; ! 434: int y2 = y1 + this_event->height; ! 435: #undef this_event ! 436: x1 /= square_size; ! 437: x2 /= square_size; ! 438: y1 /= square_size; ! 439: y2 /= square_size; ! 440: if (x2 >= squares_wide) ! 441: x2 = squares_wide - 1; /* sanity check */ ! 442: if (y2 >= squares_high) ! 443: y2 = squares_high - 1; /* sanity check */ ! 444: RepaintGridLinesPartially(x1,y1,x2+1,y2+1,e_AgainstBackground,TRUE); ! 445: RefillGridPartially (x1,y1,x2,y2,FALSE); ! 446: if (x1_square_exed_through != OUT_OF_RANGE) ! 447: ExThroughRectangle ( ! 448: max (x1, x1_square_exed_through), ! 449: max (y1, y1_square_exed_through), ! 450: min (x2, x2_square_exed_through), ! 451: min (y2, y2_square_exed_through)); ! 452: if (x1_square_plus_through != OUT_OF_RANGE) ! 453: PlusThroughRectangle ( ! 454: max (x1, x1_square_plus_through), ! 455: max (y1, y1_square_plus_through), ! 456: min (x2, x2_square_plus_through), ! 457: min (y2, y2_square_plus_through)); ! 458: if (x_hot_spot >= x1 && x_hot_spot <= x2 ! 459: && y_hot_spot >= y1 && y_hot_spot <= y2) ! 460: HighlightHotSpot(); ! 461: break; ! 462: } ! 463: ! 464: case ButtonPressed: ! 465: if (WhatSquare (event, &x_square, &y_square)) ! 466: return; /* mouse outside grid; really shouldn't happen, but... */ ! 467: switch (((XButtonPressedEvent *)event)->detail & ValueMask) { ! 468: case LeftButton: ! 469: PaintSquare (x_square, y_square, foreground); ! 470: if (x_square == x_hot_spot && y_square == y_hot_spot) ! 471: HighlightHotSpot(); ! 472: SetRasterBit (raster, x_square, y_square, 1); ! 473: break; ! 474: case MiddleButton: ! 475: InvertSquare (x_square, y_square); ! 476: InvertRasterBit (raster, x_square, y_square); ! 477: break; ! 478: case RightButton: ! 479: PaintSquare (x_square, y_square, background); ! 480: if (x_square == x_hot_spot && y_square == y_hot_spot) ! 481: HighlightHotSpot(); ! 482: SetRasterBit (raster, x_square, y_square, 0); ! 483: break; ! 484: } ! 485: RepaintRaster(); ! 486: RepaintRasterInverted(); ! 487: x_square_prev = x_square; ! 488: y_square_prev = y_square; ! 489: raster_outdated = FALSE; ! 490: changed = TRUE; ! 491: break; ! 492: ! 493: case MouseMoved: ! 494: if (WhatSquare (event, &x_square, &y_square)) ! 495: return; /* mouse outside grid; really shouldn't happen, but... */ ! 496: if ((x_square != x_square_prev) || (y_square != y_square_prev)) ! 497: switch (((XMouseMovedEvent *)event)->detail) { ! 498: case LeftMask: ! 499: PaintSquare (x_square, y_square, foreground); ! 500: if (x_square == x_hot_spot && y_square == y_hot_spot) ! 501: HighlightHotSpot(); ! 502: SetRasterBit (raster, x_square, y_square, 1); ! 503: changed = raster_outdated = TRUE; ! 504: break; ! 505: case MiddleMask: ! 506: InvertSquare (x_square, y_square); ! 507: InvertRasterBit (raster, x_square, y_square); ! 508: changed = raster_outdated = TRUE; ! 509: break; ! 510: case RightMask: ! 511: PaintSquare (x_square, y_square, background); ! 512: if (x_square == x_hot_spot && y_square == y_hot_spot) ! 513: HighlightHotSpot(); ! 514: SetRasterBit (raster, x_square, y_square, 0); ! 515: changed = raster_outdated = TRUE; ! 516: break; ! 517: default: ! 518: break; /* ignore events with multiple buttons down */ ! 519: } ! 520: if (raster_outdated && !MouseMovedEventQueued()) { ! 521: RepaintRaster(); ! 522: RepaintRasterInverted(); ! 523: raster_outdated = FALSE; ! 524: } ! 525: x_square_prev = x_square; ! 526: y_square_prev = y_square; ! 527: break; ! 528: ! 529: } ! 530: } /* end of ProcessGridWindowEvent procedure */ ! 531: ! 532: boolean MouseMovedEventQueued () { ! 533: XEvent event; ! 534: if (XPending() == 0) return (FALSE); ! 535: XPeekEvent (&event); ! 536: return (event.type == MouseMoved); ! 537: } ! 538: ! 539: ! 540: ProcessOuterWindowEvent (event) ! 541: XEvent *event; ! 542: { ! 543: if (event->type != ExposeWindow) ! 544: return; ! 545: if ((outer_frame.height == ((XExposeWindowEvent *)event)->height) ! 546: && (outer_frame.width == ((XExposeWindowEvent *)event)->width)) ! 547: /* if this isn't a resize, there's nothing to do here. */ ! 548: return; ! 549: ! 550: /* the outer window's size has changed. Must rearrange subwindows. */ ! 551: outer_frame.height = ((XExposeWindowEvent *)event)->height; ! 552: outer_frame.width = ((XExposeWindowEvent *)event)->width; ! 553: XDestroySubwindows (outer_window); ! 554: LayoutStage2 (); ! 555: XMapSubwindows (outer_window); ! 556: } ! 557: ! 558: ProcessCommandButtonEvent (command, event) ! 559: struct command_data *command; ! 560: XEvent *event; ! 561: { ! 562: static struct command_data *button_down_command; ! 563: ! 564: switch (event->type) { ! 565: ! 566: case ExposeWindow: ! 567: if (command->inverted) ! 568: XClear (command->window); ! 569: XTextMask ( ! 570: command->window, /* w */ ! 571: command->x_offset, /* x */ ! 572: 0, /* y */ ! 573: command->name, /* string */ ! 574: command->name_length, /* length */ ! 575: font, /* font */ ! 576: foreground); /* source pixel */ ! 577: if (command->inverted) ! 578: InvertCommandWindow (command); ! 579: break; ! 580: ! 581: case ButtonPressed: ! 582: if (button_down_command != NULL) ! 583: break; /* must be a second button push--ignore */ ! 584: button_down_command = command; ! 585: InvertCommandWindow (command); ! 586: command->inverted = TRUE; ! 587: break; ! 588: ! 589: case LeaveWindow: ! 590: if (command == button_down_command) { ! 591: InvertCommandWindow (command); ! 592: command->inverted = FALSE; ! 593: button_down_command = NULL; ! 594: } ! 595: break; ! 596: ! 597: case ButtonReleased: ! 598: if (command == button_down_command) { ! 599: (*command->proc)(command->data); ! 600: button_down_command = NULL; ! 601: InvertCommandWindow (command); ! 602: command->inverted = FALSE; ! 603: } ! 604: break; ! 605: ! 606: } ! 607: } ! 608: ! 609: ! 610: InvertCommandWindow (command) ! 611: struct command_data *command; ! 612: { ! 613: XPixFill ( ! 614: command->window, ! 615: 0, /* x */ ! 616: 0, /* y */ ! 617: 400, /* width = "infinity " */ ! 618: fontInfo.height, /* height */ ! 619: 1, /* pixel */ ! 620: NULL, /* clipmask bitmap */ ! 621: GXinvert, /* function */ ! 622: invertplane); /* planes */ ! 623: } /* end of InvertCommandWindow procedure */ ! 624: ! 625: ! 626: /* WhatSquare returns TRUE if mouse is outside grid, FALSE if inside. ! 627: If it returns FALSE, it assigns to *x_square and *y_square. */ ! 628: ! 629: boolean WhatSquare (event, x_square, y_square) ! 630: register XEvent *event; ! 631: register int *x_square, *y_square; /*RETURN*/ ! 632: { ! 633: int x = ((XKeyPressedEvent *)event)->x; ! 634: int y = ((XKeyPressedEvent *)event)->y; ! 635: if ((x < 0) || (y < 0)) ! 636: return (TRUE); ! 637: *x_square = x/square_size; ! 638: *y_square = y/square_size; ! 639: return ((*x_square >= squares_wide) || (*y_square >= squares_high)); ! 640: } /* end of WhatSquare procedure */ ! 641: ! 642: ! 643: RepaintGridLines(how) ! 644: enum RepaintGridType how; ! 645: { ! 646: RepaintGridLinesPartially (0, 0, squares_wide, squares_high, how, TRUE); ! 647: } ! 648: ! 649: RepaintGridLinesPartially (x1, y1, x2, y2, how, include_boundaries) ! 650: int x1, y1, x2, y2; ! 651: enum RepaintGridType how; ! 652: boolean include_boundaries; ! 653: { ! 654: register int i; ! 655: Vertex v[2]; ! 656: int pixel, func, planes; ! 657: Pattern pattern; ! 658: switch (how) { ! 659: case e_AgainstBackground: ! 660: pixel = foreground; ! 661: pattern = DottedPattern; ! 662: func = GXcopy; ! 663: planes = AllPlanes; ! 664: break; ! 665: case e_AgainstForeground: ! 666: pixel = background; ! 667: pattern = InverseDottedPattern; ! 668: func = GXcopy; ! 669: planes = AllPlanes; ! 670: break; ! 671: case e_Invert: ! 672: pixel = 1; ! 673: pattern = SolidLine; ! 674: func = GXinvert; ! 675: planes = invertplane; ! 676: break; ! 677: } ! 678: ! 679: /* draw vertical grid lines */ ! 680: v[0].flags = v[1].flags = 0; ! 681: v[0].y = y1*square_size; ! 682: v[0].y += (v[0].y & 1); /* make sure pattern is aligned on even bit boundary */ ! 683: v[1].y = y2*square_size; ! 684: if (!include_boundaries) {x1++;x2--;} ! 685: v[0].x = v[1].x = x1*square_size; ! 686: for (i=x1;i<=x2; i++) { ! 687: XDrawDashed ( ! 688: grid_window, ! 689: v, /* vertices */ ! 690: 2, /* vertex count */ ! 691: 1, /* width */ ! 692: 1, /* height */ ! 693: pixel, ! 694: pattern, ! 695: func, ! 696: planes); /* planes */ ! 697: v[0].x = (v[1].x += square_size); ! 698: } ! 699: if (!include_boundaries) {x1--;x2++;} ! 700: ! 701: /* draw horizontal grid lines */ ! 702: v[0].flags = v[1].flags = 0; ! 703: v[0].x = x1*square_size; ! 704: v[0].x += (v[0].x & 1); /* make sure pattern is aligned on even bit boundary */ ! 705: v[1].x = x2*square_size; ! 706: if (!include_boundaries) {y1++;y2--;} ! 707: v[0].y = v[1].y = y1*square_size; ! 708: for (i=y1;i<=y2;i++) { ! 709: XDrawDashed ( ! 710: grid_window, ! 711: v, /* vertices */ ! 712: 2, /* vertex count */ ! 713: 1, /* width */ ! 714: 1, /* height */ ! 715: pixel, ! 716: pattern, ! 717: func, ! 718: planes); /* planes */ ! 719: v[0].y = (v[1].y += square_size); ! 720: } ! 721: } /* end of RepaintGridLinesPartially procedure */ ! 722: ! 723: ! 724: RefillGrid (paint_background) ! 725: boolean paint_background; ! 726: { ! 727: RefillGridPartially (0, 0, squares_wide-1, squares_high-1, paint_background); ! 728: } ! 729: ! 730: ! 731: RefillGridPartially(x1, y1, x2, y2, paint_background) ! 732: register int x1, y1, x2, y2; ! 733: boolean paint_background; ! 734: { ! 735: register i, j; ! 736: for (i=x1; i<=x2; i++) { ! 737: for (j=y1; j<=y2; j++) { ! 738: bit b = GetRasterBit (raster, i, j); ! 739: if (b || paint_background) ! 740: PaintSquare (i, j, (b ? foreground : background)); ! 741: } ! 742: } ! 743: } /* end of RefillGridPartially procedure */ ! 744: ! 745: ! 746: PaintSquare(x, y, pixel) ! 747: int x, y; ! 748: int pixel; /* display function */ ! 749: { ! 750: XPixSet ( ! 751: grid_window, ! 752: x*square_size + 1, /* x */ ! 753: y*square_size + 1, /* y */ ! 754: square_size - 1, /* width */ ! 755: square_size - 1, /* height */ ! 756: pixel); /* pixel */ ! 757: } /* end of PaintSquare procedure */ ! 758: ! 759: InvertSquare(x, y) ! 760: int x, y; ! 761: { ! 762: XPixFill ( ! 763: grid_window, ! 764: x*square_size + 1, /* x */ ! 765: y*square_size + 1, /* y */ ! 766: square_size - 1, /* width */ ! 767: square_size - 1, /* height */ ! 768: 1, /* pixel */ ! 769: NULL, /* clipmask */ ! 770: GXinvert, /* function */ ! 771: invertplane); /* planes */ ! 772: } ! 773: ! 774: bit GetRasterBit (raster, x, y) ! 775: short *raster; ! 776: register int x; ! 777: int y; ! 778: { ! 779: register short *word = raster + x/16 + y*((squares_wide+15)/16); ! 780: return ( (*word & (1 << (x % 16))) ? 1 : 0); ! 781: } /* end of GetRasterBit procedure */ ! 782: ! 783: ! 784: SetRasterBit (raster, x, y, new) ! 785: short *raster; ! 786: register int x; ! 787: int y; ! 788: bit new; ! 789: { ! 790: register short *word = raster + x/16 + y*((squares_wide+15)/16); ! 791: x %= 16; ! 792: *word = (new << x) | (*word & ~(1 << x)); ! 793: } /* end of SetRasterBit procedure */ ! 794: ! 795: ! 796: InvertRasterBit (raster, x, y) ! 797: short *raster; ! 798: register int x; ! 799: int y; ! 800: { ! 801: register short *word = raster + x/16 + y*((squares_wide+15)/16); ! 802: *word ^= (1 << (x % 16)); ! 803: } /* end of InvertRasterBit procedure */ ! 804: ! 805: ! 806: RepaintRaster() { ! 807: XBitmapBitsPut ( ! 808: raster_window, ! 809: 3, /* x */ ! 810: 3, /* y */ ! 811: squares_wide, /* width */ ! 812: squares_high, /* height */ ! 813: raster, /* data */ ! 814: foreground, /* foreground */ ! 815: background, /* background */ ! 816: 0, /* mask */ ! 817: GXcopy, /* func */ ! 818: AllPlanes); /* planes */ ! 819: } /* end of RepaintRaster procedure */ ! 820: ! 821: ! 822: RepaintRasterInverted () { ! 823: XBitmapBitsPut ( ! 824: raster_invert_window, ! 825: 3, /* x */ ! 826: 3, /* y */ ! 827: squares_wide, /* width */ ! 828: squares_high, /* height */ ! 829: raster, /* data */ ! 830: background, /* foreground */ ! 831: foreground, /* background */ ! 832: 0, /* mask */ ! 833: GXcopy, /* func */ ! 834: AllPlanes); /* planes */ ! 835: } /* end of RepaintRasterInverted procedure */ ! 836: ! 837: ! 838: WriteOutputToFile (file) ! 839: FILE *file; ! 840: { ! 841: register int i; ! 842: fprintf (file, "#define %s_width %d\n", stripped_name, squares_wide); ! 843: fprintf (file, "#define %s_height %d\n", stripped_name, squares_high); ! 844: if (x_hot_spot != OUT_OF_RANGE) ! 845: fprintf (file, "#define %s_x_hot %d\n", stripped_name, x_hot_spot); ! 846: if (y_hot_spot != OUT_OF_RANGE) ! 847: fprintf (file, "#define %s_y_hot %d\n", stripped_name, y_hot_spot); ! 848: fprintf (file, "static short %s_bits[] = {\n 0x%04x", ! 849: stripped_name, (u_short) raster[0]); ! 850: ! 851: for (i=1;i<raster_length;i++) { ! 852: fprintf (file, ","); ! 853: fprintf (file, (i % 4) ? " " : "\n "); ! 854: fprintf (file, "0x%04x", (u_short) raster[i]); ! 855: } ! 856: fprintf (file, "};\n"); ! 857: } /* end of WriteOutputToFile procedure */ ! 858: ! 859: ! 860: DimensionsFromFile(file) ! 861: FILE *file; ! 862: { ! 863: char variable[81]; ! 864: int value; ! 865: while (fscanf (file, "#define %80s %2d\n", variable, &value) == 2) { ! 866: if (StringEndsWith (variable, "width")) ! 867: squares_wide = value; ! 868: else if (StringEndsWith (variable, "height")) ! 869: squares_high = value; ! 870: else if (StringEndsWith (variable, "x_hot")) ! 871: x_hot_spot = value; ! 872: else if (StringEndsWith (variable, "y_hot")) ! 873: y_hot_spot = value; ! 874: else fprintf (stderr, "Unrecognized variable '%s' in file '%s'\n", ! 875: variable, filename); ! 876: } ! 877: ! 878: if (squares_wide <= 0 || squares_wide == OUT_OF_RANGE) { ! 879: fprintf (stderr, ! 880: "%s: file '%s' does not have a valid width dimension\n", ! 881: progname, filename); ! 882: exit(1); ! 883: } ! 884: ! 885: if (squares_high <= 0 || squares_high == OUT_OF_RANGE) { ! 886: fprintf (stderr, ! 887: "%s: file '%s' does not have a valid height dimension\n", ! 888: progname, filename); ! 889: exit(1); ! 890: } ! 891: } /* end of DimensionsFromFile procedure */ ! 892: ! 893: ! 894: InitialValuesFromFile(file) ! 895: FILE *file; ! 896: { ! 897: register int i, status; ! 898: char s[81]; ! 899: ! 900: status = fscanf (file, "static short %80s = { 0x%4hx", s, &raster[0]); ! 901: if ((status != 2) || !StringEndsWith (s, "bits[]")) { ! 902: fprintf (stderr, ! 903: "%s: file '%s' has an invalid 1st array element\n", ! 904: progname, filename); ! 905: exit (1); ! 906: } ! 907: for (i=1;i<raster_length;i++) { ! 908: status = fscanf (file, ", 0x%4hx", &raster[i]); ! 909: if (status != 1) { ! 910: fprintf (stderr, ! 911: "%s: file '%s' has an invalid %dth array element\n", ! 912: progname, filename, i+1); ! 913: exit (1); ! 914: } ! 915: } ! 916: ! 917: } /* end of InitialValuesFromFile procedure */ ! 918: ! 919: ! 920: char *StripName(name) ! 921: char *name; ! 922: { ! 923: char *rindex(), *index(); ! 924: char *begin = rindex (name, '/'); ! 925: char *end, *result; ! 926: int length; ! 927: begin = (begin ? begin+1 : name); ! 928: end = index (begin, '.'); ! 929: length = (end ? (end - begin) : strlen (begin)); ! 930: result = (char *) malloc (length + 1); ! 931: strncpy (result, begin, length); ! 932: result [length] = '\0'; ! 933: return (result); ! 934: } ! 935: ! 936: ! 937: char *BackupName(name) ! 938: char *name; ! 939: { ! 940: int name_length = strlen (name); ! 941: char *result = (char *) malloc (name_length+2); ! 942: strncpy (result, name, name_length); ! 943: result [name_length] = '~'; ! 944: result [name_length+1] = '\0'; ! 945: return (result); ! 946: } ! 947: ! 948: char *TmpFileName(name) ! 949: char *name; ! 950: { ! 951: { ! 952: char *rindex(); ! 953: char *begin = rindex (name, '/'); ! 954: if (begin) ! 955: name = begin+1; ! 956: } ! 957: { ! 958: char *tmp = "/tmp/"; ! 959: int name_length = strlen (name); ! 960: int tmp_length = strlen (tmp); ! 961: int result_length = name_length + tmp_length; ! 962: char *result = (char *) malloc (result_length + 1); ! 963: strncpy (result, tmp, tmp_length); ! 964: strncpy (result+tmp_length, name, name_length); ! 965: result [result_length] = '\0'; ! 966: return (result); ! 967: } ! 968: } ! 969: ! 970: /* StringEndsWith returns TRUE if "s" ends with "suffix", else returns FALSE */ ! 971: boolean StringEndsWith (s, suffix) ! 972: char *s, *suffix; ! 973: { ! 974: int s_len = strlen (s); ! 975: int suffix_len = strlen (suffix); ! 976: return (strcmp (s + s_len - suffix_len, suffix) == 0); ! 977: } ! 978: ! 979: /* LayoutStage1 determines the size and y-position of all commmand ! 980: windows and both raster windows. It fills in everything in the ! 981: commands[] array except the "window" field, and fills in everything in ! 982: the frames[] array except for the "self" (window) and "x" fields. It ! 983: returns in *width and *bottom the dimensions of the command area to be ! 984: created. ! 985: This routine is called only once, at startup time. ! 986: Everything done at this stage stays the same even if the user later ! 987: reshapes the window. */ ! 988: ! 989: LayoutStage1 () ! 990: { ! 991: int widths [N_COMMANDS]; ! 992: int maxwidth = 0; ! 993: int ypos = TOP_MARGIN; ! 994: register int i; ! 995: register OpaqueFrame *frame; ! 996: int ClearOrSetAll(), InvertAll(), ! 997: ClearOrSetArea(), InvertArea(), CopyOrMoveArea(), ! 998: SetHotSpot(), ClearHotSpot(), ! 999: WriteOutput(), Quit(); ! 1000: ! 1001: commands[0].name = "Clear All"; ! 1002: commands[0].proc = ClearOrSetAll; ! 1003: commands[0].data = 0; ! 1004: ! 1005: commands[1].name = "Set All"; ! 1006: commands[1].proc = ClearOrSetAll; ! 1007: commands[1].data = 1; ! 1008: ! 1009: commands[2].name = "Invert All"; ! 1010: commands[2].proc = InvertAll; ! 1011: ! 1012: commands[3].name = "Clear Area"; ! 1013: commands[3].proc = ClearOrSetArea; ! 1014: commands[3].data = 0; ! 1015: ! 1016: commands[4].name = "Set Area"; ! 1017: commands[4].proc = ClearOrSetArea; ! 1018: commands[4].data = 1; ! 1019: ! 1020: commands[5].name = "Invert Area"; ! 1021: commands[5].proc = InvertArea; ! 1022: ! 1023: commands[6].name = "Copy Area"; ! 1024: commands[6].proc = CopyOrMoveArea; ! 1025: commands[6].data = 0; ! 1026: ! 1027: commands[7].name = "Move Area"; ! 1028: commands[7].proc = CopyOrMoveArea; ! 1029: commands[7].data = 1; ! 1030: ! 1031: commands[8].name = "Set HotSpot"; ! 1032: commands[8].proc = SetHotSpot; ! 1033: ! 1034: commands[9].name = "Clear HotSpot"; ! 1035: commands[9].proc = ClearHotSpot; ! 1036: ! 1037: commands[10].name = "Write Output"; ! 1038: commands[10].proc = WriteOutput; ! 1039: ! 1040: commands[11].name = "Quit"; ! 1041: commands[11].proc = Quit; ! 1042: ! 1043: ! 1044: for (i=0;i<N_COMMANDS;i++) { ! 1045: widths[i] = XQueryWidth (commands[i].name, font); ! 1046: if (maxwidth < widths[i]) ! 1047: maxwidth = widths[i]; ! 1048: } ! 1049: ! 1050: maxwidth += 4; /* so even widest command has a little space around it */ ! 1051: ! 1052: for (i=0;i<N_COMMANDS;i++) { ! 1053: register struct command_data *command = &commands[i]; ! 1054: frame = &frames[i]; ! 1055: command->name_length = strlen (command->name); ! 1056: command->x_offset = (maxwidth - widths[i])/2; ! 1057: frame->y = ypos; ! 1058: frame->width = maxwidth; ! 1059: frame->height = fontInfo.height; ! 1060: frame->bdrwidth = 1; ! 1061: frame->border = border; ! 1062: frame->background = backmap; ! 1063: ypos += fontInfo.height + 5; ! 1064: if (i==2 || i == 5 || i == 7 || i == 9) ! 1065: ypos += fontInfo.height + 5; ! 1066: /* for gaps between groups; pretty random! */ ! 1067: ! 1068: } ! 1069: ! 1070: /* set up raster window */ ! 1071: frame = &frame[N_COMMANDS]; ! 1072: frame = &frames[i]; ! 1073: frame->y = (ypos += AROUND_RASTER_MARGIN); ! 1074: frame->width = squares_wide + 6; ! 1075: frame->height = squares_high + 6; ! 1076: frame->bdrwidth = 1; ! 1077: frame->border = border; ! 1078: frame->background = backmap; ! 1079: ! 1080: /* raster invert window is the same, except for y position */ ! 1081: *(frame+1) = *frame; ! 1082: (++frame)->y += squares_high + 8 + AROUND_RASTER_MARGIN; ! 1083: ! 1084: } ! 1085: ! 1086: /* LayoutStage2 is called both at startup time and whenever the user ! 1087: resizes the outer window. It figures out what the new grid square size ! 1088: should be, determines the size and position of all subwindows, then ! 1089: creates (but does not map) the subwindows. */ ! 1090: ! 1091: LayoutStage2 () ! 1092: { ! 1093: int x_room = outer_frame.width - 1 ! 1094: - LEFT_MARGIN - frames[0].width - GRID_TO_COMMAND_MARGIN - RIGHT_MARGIN; ! 1095: int y_room = outer_frame.height - 1 ! 1096: - TOP_MARGIN - BOTTOM_MARGIN; ! 1097: int i; ! 1098: int command_x_offset; ! 1099: OpaqueFrame *grid_frame = &frames[N_COMMANDS+2]; ! 1100: ! 1101: x_room /= squares_wide; ! 1102: y_room /= squares_high; ! 1103: square_size = min (x_room, y_room); ! 1104: ! 1105: /* fill in the grid window's frame */ ! 1106: grid_frame->x = LEFT_MARGIN; ! 1107: grid_frame->y = TOP_MARGIN; ! 1108: grid_frame->width = (squares_wide * square_size) + 1; ! 1109: grid_frame->height = (squares_high * square_size) + 1; ! 1110: grid_frame->bdrwidth = 0; ! 1111: grid_frame->border = NULL; ! 1112: grid_frame->background = backmap; ! 1113: ! 1114: /* fill in x offsets for command window frames */ ! 1115: command_x_offset = grid_frame->x + grid_frame->width ! 1116: + GRID_TO_COMMAND_MARGIN; ! 1117: for (i=0;i<N_COMMANDS;i++) ! 1118: frames[i].x = command_x_offset; ! 1119: ! 1120: /* fill in x offsets for raster and raster-inverted window frames */ ! 1121: frames[N_COMMANDS].x = frames[N_COMMANDS+1].x = ! 1122: grid_frame->x + grid_frame->width + AROUND_RASTER_MARGIN; ! 1123: ! 1124: /* create all the subwindows */ ! 1125: XCreateWindows (outer_window, frames, N_COMMANDS+3); ! 1126: ! 1127: /* stow away all the resulting window id's, and select input */ ! 1128: for (i=0;i<N_COMMANDS;i++) ! 1129: XSelectInput (commands[i].window = frames[i].self, ! 1130: ButtonPressed | ButtonReleased | LeaveWindow | ExposeWindow); ! 1131: ! 1132: XSelectInput (raster_window = frames[N_COMMANDS].self, ExposeWindow); ! 1133: XSelectInput (raster_invert_window = frames[N_COMMANDS+1].self, ! 1134: ExposeWindow); ! 1135: XSelectInput (grid_window = grid_frame->self, ! 1136: RightDownMotion | MiddleDownMotion | LeftDownMotion ! 1137: | ExposeRegion | ButtonPressed | ButtonReleased); ! 1138: /* ButtonReleased is selected for AskUserForArea's benefit */ ! 1139: ! 1140: } ! 1141: ! 1142: /* OuterWindowDims determines the minimum size for the outer window, ! 1143: based on three constraints: the grid square size, the width of ! 1144: the command/raster area, and the minimum height of the ! 1145: command/raster area ("right side" of the window). It is called ! 1146: at startup time. */ ! 1147: ! 1148: OuterWindowDims (square_size, right_side_width, ! 1149: right_side_bottom, width, height) ! 1150: int square_size, right_side_width, right_side_bottom; ! 1151: int *width, *height; /* RETURN */ ! 1152: { ! 1153: *width = LEFT_MARGIN + squares_wide*square_size + 1 + right_side_width; ! 1154: *height = TOP_MARGIN + squares_high*square_size + 1 + BOTTOM_MARGIN; ! 1155: if (*height < right_side_bottom) ! 1156: *height = right_side_bottom; ! 1157: } ! 1158: ! 1159: ! 1160: ClearOrSetAll(b) ! 1161: bit b; /* 0 for clear, 1 for set */ ! 1162: { ! 1163: register int i; ! 1164: register int new = (b ? ~0: 0); ! 1165: for (i=0;i<raster_length;i++) ! 1166: raster[i] = new; ! 1167: changed = TRUE; ! 1168: XPixSet ( ! 1169: grid_window, /* window */ ! 1170: 0, /* x */ ! 1171: 0, /* y */ ! 1172: squares_wide*square_size+1, /* width */ ! 1173: squares_high*square_size+1, /* height */ ! 1174: b ? foreground : background); ! 1175: RepaintGridLines (b ? e_AgainstForeground : e_AgainstBackground); ! 1176: RepaintRaster(); ! 1177: RepaintRasterInverted(); ! 1178: if (x_hot_spot != OUT_OF_RANGE) ! 1179: HighlightHotSpot(); ! 1180: } /* end of ClearAll procedure */ ! 1181: ! 1182: ! 1183: ClearOrSetArea(b) ! 1184: bit b; /* 0 for clear, 1 for set */ ! 1185: { ! 1186: int x1, y1, x2, y2; ! 1187: register int x, y; ! 1188: if (AskUserForArea (&x1, &y1, &x2, &y2)) ! 1189: return; ! 1190: for (x=x1;x<=x2;x++) ! 1191: for (y=y1;y<=y2;y++) ! 1192: SetRasterBit (raster, x, y, b); ! 1193: XPixSet ( ! 1194: grid_window, /* window */ ! 1195: x1*square_size+1, /* x */ ! 1196: y1*square_size+1, /* y */ ! 1197: (x2-x1+1)*square_size-1, /* width */ ! 1198: (y2-y1+1)*square_size-1, /* height */ ! 1199: b ? foreground : background); ! 1200: RepaintGridLinesPartially (x1, y1, x2+1, y2+1, b ? e_AgainstForeground : e_AgainstBackground, FALSE); ! 1201: if (x_hot_spot >= x1 && x_hot_spot <= x2 ! 1202: && y_hot_spot >= y1 && y_hot_spot <= y2) ! 1203: HighlightHotSpot(); ! 1204: changed = TRUE; ! 1205: RepaintRaster(); ! 1206: RepaintRasterInverted(); ! 1207: x1_square_exed_through = y1_square_exed_through = OUT_OF_RANGE; ! 1208: x2_square_exed_through = y2_square_exed_through = OUT_OF_RANGE; ! 1209: } /* end of ClearArea procedure */ ! 1210: ! 1211: ! 1212: InvertAll() { ! 1213: register int i; ! 1214: for (i=0;i<raster_length;i++) ! 1215: raster[i] ^= ~0; /* invert = exclusive or with all 1's */ ! 1216: changed = TRUE; ! 1217: XPixFill ( ! 1218: grid_window, /* window */ ! 1219: 0, /* x */ ! 1220: 0, /* y */ ! 1221: squares_wide*square_size+1, /* width */ ! 1222: squares_high*square_size+1, /* height */ ! 1223: 1, /* pixel */ ! 1224: NULL, /* clipmask */ ! 1225: GXinvert, /* function */ ! 1226: invertplane); /* plane mask */ ! 1227: RepaintGridLines (e_Invert); ! 1228: RepaintRaster(); ! 1229: RepaintRasterInverted(); ! 1230: } /* end of InvertAll procedure */ ! 1231: ! 1232: ! 1233: InvertArea() { ! 1234: int x1, y1, x2, y2; ! 1235: register int x, y; ! 1236: if (AskUserForArea (&x1, &y1, &x2, &y2)) ! 1237: return; ! 1238: for (x=x1;x<=x2;x++) ! 1239: for (y=y1;y<=y2;y++) ! 1240: InvertRasterBit (raster, x, y); ! 1241: ExThroughRectangle (x1, y1, x2, y2); /* wipe out X-outs */ ! 1242: XPixFill ( ! 1243: grid_window, /* window */ ! 1244: x1*square_size+1, /* x */ ! 1245: y1*square_size+1, /* y */ ! 1246: (x2-x1+1)*square_size-1, /* width */ ! 1247: (y2-y1+1)*square_size-1, /* height */ ! 1248: 1, /* pixel */ ! 1249: NULL, /* clipmask */ ! 1250: GXinvert, /* function */ ! 1251: invertplane); /* plane mask */ ! 1252: RepaintGridLinesPartially (x1, y1, x2+1, y2+1, e_Invert, FALSE); ! 1253: changed = TRUE; ! 1254: RepaintRaster(); ! 1255: RepaintRasterInverted(); ! 1256: x1_square_exed_through = y1_square_exed_through = OUT_OF_RANGE; ! 1257: x2_square_exed_through = y2_square_exed_through = OUT_OF_RANGE; ! 1258: } /* end of InvertArea procedure */ ! 1259: ! 1260: ! 1261: CopyOrMoveArea (move) ! 1262: boolean move; ! 1263: { ! 1264: int x1, y1, x2, y2; ! 1265: int x1dest, y1dest; ! 1266: if (AskUserForArea (&x1, &y1, &x2, &y2)) ! 1267: return; ! 1268: if (AskUserForDest (&x1dest, &y1dest, x2-x1+1, y2-y1+1)) ! 1269: /* button released outside grid */ ! 1270: ExThroughRectangle (x1_square_exed_through, y1_square_exed_through, ! 1271: x2_square_exed_through, y2_square_exed_through); ! 1272: else { ! 1273: register int xsrc, ysrc, xdest, ydest; ! 1274: register short *new_raster = ! 1275: (short *) malloc (raster_length*sizeof(short)); ! 1276: ! 1277: if (x_hot_spot != OUT_OF_RANGE) ! 1278: HighlightHotSpot(); /* actually UNhighlight it */ ! 1279: ! 1280: /* copy whole raster to new raster */ ! 1281: bcopy (raster, new_raster, raster_length*sizeof(short)); ! 1282: ! 1283: if (move) ! 1284: /* clear source bits in new raster. this is VERY inefficient. ! 1285: sure wish we had BitBlt available in user memory! */ ! 1286: for (xsrc = x1; xsrc <= x2; xsrc++) ! 1287: for (ysrc = y1; ysrc <= y2; ysrc++) ! 1288: SetRasterBit (new_raster, xsrc, ysrc, 0); ! 1289: ! 1290: /* copy old source bits to new destination. this is VERY inefficient. ! 1291: sure wish we had BitBlt available in user memory! */ ! 1292: ! 1293: for (xsrc = x1, xdest = x1dest; ! 1294: xsrc<=x2 && xdest < squares_wide; xsrc++, xdest++) ! 1295: for (ysrc = y1, ydest = y1dest; ! 1296: ysrc<=y2 && ydest < squares_high; ysrc++, ydest++) ! 1297: SetRasterBit (new_raster, xdest, ydest, ! 1298: GetRasterBit (raster, xsrc, ysrc)); ! 1299: ! 1300: free (raster); ! 1301: raster = new_raster; ! 1302: if (move) ! 1303: RepaintRectangles (x1, y1, x2, y2, x1dest, y1dest); ! 1304: else { ! 1305: ExThroughRectangle (x1_square_exed_through, y1_square_exed_through, ! 1306: x2_square_exed_through, y2_square_exed_through); ! 1307: RefillGridPartially (x1dest, y1dest, xdest-1, ydest-1, TRUE); ! 1308: } ! 1309: ! 1310: if (x_hot_spot != OUT_OF_RANGE) ! 1311: HighlightHotSpot(); /* put the hot spot back on the screen */ ! 1312: ! 1313: RepaintRaster(); ! 1314: RepaintRasterInverted(); ! 1315: changed = TRUE; ! 1316: x1_square_plus_through = y1_square_plus_through = OUT_OF_RANGE; ! 1317: x2_square_plus_through = y2_square_plus_through = OUT_OF_RANGE; ! 1318: } ! 1319: ! 1320: x1_square_exed_through = y1_square_exed_through = OUT_OF_RANGE; ! 1321: x2_square_exed_through = y2_square_exed_through = OUT_OF_RANGE; ! 1322: } /* end of CopyArea procedure */ ! 1323: ! 1324: ! 1325: ClearHotSpot() { ! 1326: if (x_hot_spot == OUT_OF_RANGE) ! 1327: return; ! 1328: HighlightHotSpot(); /* UNhighlight existing hot spot */ ! 1329: x_hot_spot = y_hot_spot = OUT_OF_RANGE; ! 1330: changed = TRUE; ! 1331: } ! 1332: ! 1333: SetHotSpot() { ! 1334: XCompressEvents(); ! 1335: XDefineCursor (outer_window, dot); ! 1336: XSelectInput (outer_window, ButtonPressed | ButtonReleased | ExposeWindow); ! 1337: /* so that we can detect button pressed outside grid */ ! 1338: ! 1339: while (TRUE) { ! 1340: XEvent event; ! 1341: int x1, y1; ! 1342: XNextEvent (&event); ! 1343: switch (event.type) { ! 1344: ! 1345: case ButtonPressed: ! 1346: case MouseMoved: ! 1347: if ((event.window == grid_window) ! 1348: && !WhatSquare (&event, &x1, &y1) ! 1349: && (x_hot_spot != x1 || y_hot_spot != y1)) { ! 1350: ! 1351: /* UNhighlight old hot spot */ ! 1352: if (x_hot_spot != OUT_OF_RANGE) ! 1353: HighlightHotSpot(); ! 1354: ! 1355: x_hot_spot = x1; ! 1356: y_hot_spot = y1; ! 1357: ! 1358: /* highlight new hot spot */ ! 1359: HighlightHotSpot(); ! 1360: ! 1361: changed = TRUE; ! 1362: } ! 1363: break; /* keep looping until button is released */ ! 1364: ! 1365: case ButtonReleased: ! 1366: XExpandEvents(); ! 1367: XDefineCursor (outer_window, cross); ! 1368: XSelectInput (outer_window, ExposeWindow); ! 1369: return; ! 1370: ! 1371: case ExposeWindow: ! 1372: case ExposeRegion: ! 1373: ProcessEvent (&event); ! 1374: break; ! 1375: ! 1376: default: ! 1377: break; /* just throw it away */ ! 1378: ! 1379: } ! 1380: } ! 1381: } ! 1382: ! 1383: RepaintRectangles (x1, y1, x2, y2, x3, y3) ! 1384: int x1, y1; /* first rectangle's top & left */ ! 1385: int x2, y2; /* first rectangle's bottom & right */ ! 1386: int x3, y3; /* second rectangle's top & left */ ! 1387: { ! 1388: int x4 = x3 + (x2 - x1); /* second rectangle's right edge */ ! 1389: int y4 = y3 + (y2 - y1); /* second rectangle's bottom edge */ ! 1390: ! 1391: if (x4 >= squares_wide) x4 = squares_wide-1; ! 1392: if (y4 >= squares_wide) y4 = squares_high-1; ! 1393: ! 1394: /* if first rectangle is right of second, swap "first" and "second" variables */ ! 1395: if (x1 > x3) ! 1396: {int temp; ! 1397: #define swap(a,b) {temp = a; a = b; b = temp;} ! 1398: swap (x1, x3); swap (y1, y3); swap (x2, x4); swap (y2, y4); ! 1399: #undef swap ! 1400: } ! 1401: ! 1402: RefillGridPartially (x1, y1, x2, y2, TRUE); ! 1403: ! 1404: if ((x3 > x2) || (max (y1, y3) > min (y2, y4))) ! 1405: /* rectangles don't overlap */ ! 1406: RefillGridPartially (x3, y3, x4, y4, TRUE); ! 1407: ! 1408: else if (y1 < y3) { ! 1409: /* second rectangle is below & right of first */ ! 1410: RefillGridPartially (x2+1, y3, x4, y2, TRUE); ! 1411: RefillGridPartially (x3, y2+1, x4, y4, TRUE); ! 1412: } ! 1413: ! 1414: else { ! 1415: /* second rectangle is above & right of first */ ! 1416: RefillGridPartially (x3, y3, x4, y1-1, TRUE); ! 1417: RefillGridPartially (x2+1, y1, x4, y4, TRUE); ! 1418: } ! 1419: } ! 1420: ! 1421: ! 1422: /* AskUserForArea returns FALSE if the user has defined a valid area, ! 1423: TRUE if the user hasn't (e.g. by clicking outside grid) */ ! 1424: ! 1425: boolean AskUserForArea(px1, py1, px2, py2) ! 1426: int *px1, *py1, *px2, *py2; ! 1427: { ! 1428: XEvent event; ! 1429: int x1, y1, x2, y2; ! 1430: boolean result; ! 1431: ! 1432: XSelectInput (outer_window, ButtonPressed | ExposeWindow); ! 1433: /* so that we can detect button pressed outside grid */ ! 1434: ! 1435: XDefineCursor (outer_window, upper_left); ! 1436: ! 1437: while (TRUE) { ! 1438: XNextEvent (&event); ! 1439: switch (event.type) { ! 1440: case ButtonPressed: ! 1441: if ((event.window != grid_window) ! 1442: || WhatSquare (&event, &x1, &y1)) { ! 1443: XDefineCursor (outer_window, cross); ! 1444: XSelectInput (outer_window, ExposeWindow); ! 1445: return (TRUE); ! 1446: } ! 1447: goto out1; /* get out of the loop */ ! 1448: case ExposeWindow: ! 1449: case ExposeRegion: ! 1450: ProcessEvent (&event); ! 1451: break; ! 1452: default: ! 1453: break; /* just throw it away */ ! 1454: } ! 1455: } ! 1456: ! 1457: out1: ! 1458: XCompressEvents(); /* DO collapse consecutive MouseMoved events */ ! 1459: ExThroughSquare (x1, y1); ! 1460: FlushLineBuffer(); ! 1461: x1_square_exed_through = x2_square_exed_through = x2 = x1; ! 1462: y1_square_exed_through = y2_square_exed_through = y2 = y1; ! 1463: XDefineCursor (outer_window, lower_right); ! 1464: ! 1465: while (TRUE) { ! 1466: XNextEvent (&event); ! 1467: switch (event.type) { ! 1468: case ButtonPressed: ! 1469: result = TRUE; ! 1470: goto out2; ! 1471: case ExposeWindow: ! 1472: case ExposeRegion: ! 1473: ProcessEvent (&event); ! 1474: break; ! 1475: case MouseMoved: ! 1476: case ButtonReleased: { ! 1477: int x, y; ! 1478: result = (event.window != grid_window) ! 1479: || WhatSquare (&event, &x, &y) /* mouse outside grid? */ ! 1480: || (x < x1) || (y < y1); ! 1481: if (result) { ! 1482: ExThroughRectangle (x1+1, y1, x2, y2); ! 1483: ExThroughRectangle (x1, y1+1, x1, y2); ! 1484: x2 = x2_square_exed_through = x1; ! 1485: y2 = y2_square_exed_through = y1; ! 1486: } ! 1487: else if ((x == x2) && (y == y2)) ! 1488: ; /* both dimensions the same; do nothing */ ! 1489: else if ((x > x2) == (y > y2)) { ! 1490: /* both dimensions bigger or smaller */ ! 1491: ExThroughRectangle (min(x2,x)+1, y1, max(x2,x), max(y2,y)); ! 1492: ExThroughRectangle (x1, min(y2,y)+1, min(x2,x), max(y2,y)); ! 1493: x2 = x2_square_exed_through = x; ! 1494: y2 = y2_square_exed_through = y; ! 1495: } ! 1496: else { ! 1497: /* one dimension bigger, the other smaller */ ! 1498: ExThroughRectangle (min(x2,x)+1, y1, max(x2,x), min(y2,y)); ! 1499: ExThroughRectangle (x1, min(y2,y)+1, min(x2,x), max(y2,y)); ! 1500: x2 = x2_square_exed_through = x; ! 1501: y2 = y2_square_exed_through = y; ! 1502: } ! 1503: if (event.type == ButtonReleased) ! 1504: goto out2; ! 1505: break; ! 1506: } ! 1507: default: ! 1508: break; /* just throw it away */ ! 1509: } ! 1510: } ! 1511: ! 1512: out2: ! 1513: XSelectInput (outer_window, ExposeWindow); ! 1514: XDefineCursor (outer_window, cross); ! 1515: if (result) { ! 1516: /* no area properly selected; remove X-outs from display */ ! 1517: ExThroughRectangle (x1, y1, x2, y2); ! 1518: x1_square_exed_through = y1_square_exed_through = OUT_OF_RANGE; ! 1519: x2_square_exed_through = y2_square_exed_through = OUT_OF_RANGE; ! 1520: } ! 1521: else { ! 1522: *px1 = x1; ! 1523: *px2 = x2; ! 1524: *py1 = y1; ! 1525: *py2 = y2; ! 1526: } ! 1527: XExpandEvents(); ! 1528: return (result); ! 1529: } /* end of AskUserForArea procedure */ ! 1530: ! 1531: ! 1532: boolean AskUserForDest (px1, py1, width, height) ! 1533: int *px1, *py1; ! 1534: int width, height; ! 1535: { ! 1536: XEvent event; ! 1537: boolean result; ! 1538: XCompressEvents(); /* DO collapse consecutive MouseMoved events */ ! 1539: XSelectInput (outer_window, ButtonPressed | ButtonReleased | ExposeWindow); ! 1540: /* so we can detect button action outside grid */ ! 1541: XDefineCursor (outer_window, upper_left); ! 1542: ! 1543: while (TRUE) { ! 1544: XNextEvent (&event); ! 1545: switch (event.type) { ! 1546: ! 1547: case ExposeWindow: ! 1548: case ExposeRegion: ! 1549: ProcessEvent (&event); ! 1550: break; ! 1551: ! 1552: case ButtonPressed: ! 1553: case MouseMoved: { ! 1554: int x1_new, y1_new; ! 1555: boolean this_window = (event.window == grid_window) ! 1556: && !WhatSquare (&event, &x1_new, &y1_new); ! 1557: ! 1558: if (this_window && (x1_new == *px1) && (y1_new == *py1)) ! 1559: break; /* mouse is still in same square as before; do nothing */ ! 1560: ! 1561: if (x1_square_plus_through != OUT_OF_RANGE) ! 1562: PlusThroughRectangle (x1_square_plus_through, y1_square_plus_through, ! 1563: x2_square_plus_through, y2_square_plus_through); ! 1564: ! 1565: if (this_window) { ! 1566: *px1 = x1_square_plus_through = x1_new; ! 1567: *py1 = y1_square_plus_through = y1_new; ! 1568: x2_square_plus_through = min (x1_new + width, squares_wide) - 1; ! 1569: y2_square_plus_through = min (y1_new + height, squares_high) - 1; ! 1570: PlusThroughRectangle (x1_square_plus_through, y1_square_plus_through, ! 1571: x2_square_plus_through, y2_square_plus_through); ! 1572: } ! 1573: else { ! 1574: x1_square_plus_through = y1_square_plus_through = OUT_OF_RANGE; ! 1575: x2_square_plus_through = y2_square_plus_through = OUT_OF_RANGE; ! 1576: *px1 = *py1 = OUT_OF_RANGE; ! 1577: } ! 1578: break; ! 1579: } ! 1580: ! 1581: case ButtonReleased: { ! 1582: result = (event.window != grid_window) ! 1583: || WhatSquare (&event, px1, py1); ! 1584: goto out; ! 1585: } ! 1586: ! 1587: default: ! 1588: break; /* throw it away */ ! 1589: } ! 1590: } ! 1591: ! 1592: out: ! 1593: if (result) { ! 1594: /* button released outside grid */ ! 1595: if (x1_square_plus_through != OUT_OF_RANGE) ! 1596: PlusThroughRectangle (x1_square_plus_through, y1_square_plus_through, ! 1597: x2_square_plus_through, y2_square_plus_through); ! 1598: x1_square_plus_through = y1_square_plus_through = OUT_OF_RANGE; ! 1599: x2_square_plus_through = y2_square_plus_through = OUT_OF_RANGE; ! 1600: } ! 1601: ! 1602: XExpandEvents(); ! 1603: XSelectInput (outer_window, ExposeWindow); ! 1604: XDefineCursor (outer_window, cross); ! 1605: return (result); ! 1606: } /* end of AskUserForDest procedure */ ! 1607: ! 1608: ! 1609: DialogInputHandler (event) ! 1610: XEvent *event; ! 1611: { ! 1612: switch (event->type) { ! 1613: case ExposeWindow: ! 1614: case ExposeRegion: ! 1615: ProcessEvent (event); ! 1616: } ! 1617: } ! 1618: ! 1619: enum output_error {e_rename, e_write}; ! 1620: ! 1621: /* WriteOutput returns TRUE if output successfully written, FALSE if not */ ! 1622: ! 1623: WriteOutput() { ! 1624: FILE *file; ! 1625: if (!changed) ! 1626: return (TRUE); ! 1627: if (rename (filename, backup_filename) && errno != ENOENT) ! 1628: return (HandleOutputError(e_rename)); ! 1629: file = fopen (filename, "w+"); ! 1630: if (!file) ! 1631: return (HandleOutputError(e_write)); ! 1632: WriteOutputToFile (file); ! 1633: fclose (file); ! 1634: changed = FALSE; ! 1635: return (TRUE); ! 1636: } ! 1637: ! 1638: ! 1639: /* HandleOutputError returns TRUE if alternate file written, FALSE if not */ ! 1640: ! 1641: int HandleOutputError(e) ! 1642: enum output_error e; ! 1643: { ! 1644: int result; ! 1645: char *strings[2]; ! 1646: char msg1[120], msg2[120]; ! 1647: char *tmp_filename; ! 1648: if (e == e_rename) ! 1649: sprintf (msg1, "Can't rename %s to %s -- %s", ! 1650: filename, backup_filename, sys_errlist[errno]); ! 1651: else ! 1652: sprintf (msg1, "Can't write on file %s -- %s", ! 1653: filename, sys_errlist[errno]); ! 1654: tmp_filename = TmpFileName (filename); ! 1655: sprintf (msg2, "Should I write output to file %s?", tmp_filename); ! 1656: strings[0] = "Yes"; ! 1657: strings[1] = "No"; ! 1658: result = dialog (outer_window, font, fontInfo.height, ! 1659: msg1, msg2, strings, 2, DialogInputHandler); ! 1660: ! 1661: if (result == 0) /* "yes" */ { ! 1662: filename = tmp_filename; ! 1663: free (backup_filename); ! 1664: backup_filename = BackupName (filename); ! 1665: return (WriteOutput()); ! 1666: } ! 1667: else { /* "no" */ ! 1668: free (tmp_filename); ! 1669: return (FALSE); ! 1670: } ! 1671: } ! 1672: ! 1673: ! 1674: Quit() { ! 1675: if (changed) { ! 1676: int result; ! 1677: char *strings[3]; ! 1678: strings[0] = "Yes"; ! 1679: strings[1] = "No"; ! 1680: strings[2] = "Cancel"; ! 1681: result = dialog (outer_window, font, fontInfo.height, ! 1682: "Save changes before quitting?", "", strings, 3, DialogInputHandler); ! 1683: ! 1684: switch (result) { ! 1685: case 0: /* "yes" */ ! 1686: if (WriteOutput()) ! 1687: exit(0); ! 1688: else return; ! 1689: case 1: /* "no" */ ! 1690: exit(0); ! 1691: default: /* "cancel" */ ! 1692: return; ! 1693: } ! 1694: } ! 1695: ! 1696: exit(0); ! 1697: } ! 1698: ! 1699: HighlightHotSpot() { ! 1700: /* Draw a diamond in the hot spot square */ ! 1701: /* x1 and y1 are the center of the hot spot square */ ! 1702: register int x1 = x_hot_spot*square_size + square_size/2; ! 1703: register int y1 = y_hot_spot*square_size + square_size/2; ! 1704: register int radius = square_size/6; ! 1705: register int i; ! 1706: Vertex v[5]; ! 1707: v[0].x = v[2].x = v[4].x = x1; ! 1708: v[1].x = x1 + radius; ! 1709: v[3].x = x1 - radius; ! 1710: v[0].y = v[4].y = y1 + radius; ! 1711: v[1].y = v[3].y = y1; ! 1712: v[2].y = y1 - radius; ! 1713: for (i=0;i<5;i++) ! 1714: v[i].flags = 0; ! 1715: XDraw (grid_window, v, 5, 1, 1, 1, GXinvert, highlightplane); ! 1716: } ! 1717: ! 1718: ExThroughRectangle (x1, y1, x2, y2) ! 1719: register int x1, y1, x2, y2; ! 1720: { ! 1721: register int x, y; ! 1722: for (x=x1;x<=x2;x++) ! 1723: for (y=y1;y<=y2;y++) ! 1724: ExThroughSquare (x, y); ! 1725: FlushLineBuffer(); ! 1726: } ! 1727: ! 1728: ! 1729: ExThroughSquare (x, y) ! 1730: register int x, y; ! 1731: { ! 1732: register int x1 = x*square_size; ! 1733: register int y1 = y*square_size; ! 1734: LineIntoBuffer (x1+1, y1+1, ! 1735: x1+square_size, y1+square_size); ! 1736: LineIntoBuffer (x1+square_size-1, y1+1, ! 1737: x1, y1+square_size); ! 1738: } ! 1739: ! 1740: ! 1741: PlusThroughRectangle (x1, y1, x2, y2) ! 1742: register int x1, y1, x2, y2; ! 1743: { ! 1744: register int x, y; ! 1745: for (x=x1;x<=x2;x++) ! 1746: for (y=y1;y<=y2;y++) ! 1747: PlusThroughSquare (x, y); ! 1748: FlushLineBuffer(); ! 1749: } ! 1750: ! 1751: PlusThroughSquare (x, y) ! 1752: register int x, y; ! 1753: { ! 1754: register int x1 = x*square_size; ! 1755: register int y1 = y*square_size; ! 1756: LineIntoBuffer (x1+square_size/2, y1+1, ! 1757: x1+square_size/2, y1+square_size); ! 1758: LineIntoBuffer (x1+1, y1+square_size/2, ! 1759: x1+square_size, y1+square_size/2); ! 1760: } ! 1761: ! 1762: ! 1763: #define BUFFER_MAXLENGTH 200 /* must be even */ ! 1764: static Vertex buffer [BUFFER_MAXLENGTH]; ! 1765: static int buffer_length = 0; ! 1766: ! 1767: LineIntoBuffer (x1, y1, x2, y2) { ! 1768: buffer [buffer_length].x = x1; ! 1769: buffer [buffer_length].y = y1; ! 1770: buffer [buffer_length++].flags = VertexDontDraw; ! 1771: buffer [buffer_length].x = x2; ! 1772: buffer [buffer_length].y = y2; ! 1773: buffer [buffer_length++].flags = 0; ! 1774: if (buffer_length == BUFFER_MAXLENGTH) ! 1775: FlushLineBuffer(); ! 1776: } ! 1777: ! 1778: FlushLineBuffer () { ! 1779: XDraw (grid_window, buffer, buffer_length, 1, 1, 1, GXinvert,highlightplane); ! 1780: buffer_length = 0; ! 1781: } ! 1782: ! 1783: #ifdef romp ! 1784: /* ! 1785: * prerelease IBM RT/PC software does not have ffs in its C library. ! 1786: * This code should be thrown away by summer, 1986. ! 1787: */ ! 1788: int ffs(i) ! 1789: int i; ! 1790: { ! 1791: int j = 1; ! 1792: if (i == 0) return (0); ! 1793: while (1) { ! 1794: if (i & 1) return (j); ! 1795: j++; ! 1796: i >>= 1; ! 1797: } ! 1798: } ! 1799: ! 1800: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.