Annotation of 43BSD/contrib/X/uwm/Menu.c, revision 1.1

1.1     ! root        1: #ifndef lint
        !             2: static char *rcsid_Menu_c = "$Header: Menu.c,v 10.3 86/02/01 16:23:11 tony Rel $";
        !             3: #endif lint
        !             4: 
        !             5: /************************************************************************
        !             6:  *                                                                     *
        !             7:  *                     Copyright (c) 1986 by                           *
        !             8:  *             Digital Equipment Corporation, Maynard, MA              *
        !             9:  *                      All Rights Reserved.                           *
        !            10:  *                                                                     *
        !            11:  *     Permission to use, copy, modify, and distribute this software   *
        !            12:  *     and its documentation is hereby granted only to licensees of    *
        !            13:  *     The Regents of the University of California pursuant to their   *
        !            14:  *     license agreement for the Berkeley Software Distribution        *
        !            15:  *     provided that the following appears on all copies.              *
        !            16:  *                                                                     *
        !            17:  *            "LICENSED FROM DIGITAL EQUIPMENT CORPORATION             *
        !            18:  *                      COPYRIGHT (C) 1986                             *       
        !            19:  *                 DIGITAL EQUIPMENT CORPORATION                       *
        !            20:  *                         MAYNARD, MA                                 *
        !            21:  *                     ALL RIGHTS RESERVED.                            *
        !            22:  *                                                                     *
        !            23:  *      THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT  * 
        !            24:  *     NOTICE AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL   *
        !            25:  *     EQUIPMENT CORPORATION.  DIGITAL MAKES NO REPRESENTATIONS        *
        !            26:  *     ABOUT SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. IT IS       *
        !            27:  *     SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.           *
        !            28:  *                                                                     *       
        !            29:  *     IF THE UNIVERSITY OF CALIFORNIA OR ITS LICENSEES MODIFY         *       
        !            30:  *     THE SOFTWARE IN A MANNER CREATING DERIVATIVE COPYRIGHT          *       
        !            31:  *     RIGHTS APPROPRIATE COPYRIGHT LEGENDS MAY BE PLACED ON THE       *
        !            32:  *     DERIVATIVE WORK IN ADDITION TO THAT SET FORTH ABOVE."           *       
        !            33:  *                                                                     *
        !            34:  ************************************************************************/
        !            35:  
        !            36: 
        !            37: /*
        !            38:  * MODIFICATION HISTORY
        !            39:  *
        !            40:  * 000 -- M. Gancarz, DEC Ultrix Engineering Group
        !            41:  */
        !            42: 
        !            43: #ifndef lint
        !            44: static char *sccsid = "@(#)Menu.c      3.8     1/24/86";
        !            45: #endif
        !            46: 
        !            47: #include "uwm.h"
        !            48: 
        !            49: #define DisplayLine(w, pane, width, height, str, fg, bg) \
        !            50:          XPixSet(w, 0, pane, width, height, bg); \
        !            51:          XTextMask(w, HMenuPad, pane + VMenuPad, str, strlen(str), MFont, fg);
        !            52: 
        !            53: #define NVERTS 5                       /* Number of vertices for hi-liter. */
        !            54: 
        !            55: static Vertex vlist[NVERTS];           /* Vertex list for hi-liter. */
        !            56: 
        !            57: Bool Menu(window, mask, button, x, y, menu)
        !            58: Window window;                         /* Event window. */
        !            59: int mask;                              /* Button/key mask. */
        !            60: short button;                          /* Button event detail. */
        !            61: int x, y;                              /* Event mouse position. */
        !            62: MenuInfo *menu;
        !            63: {
        !            64:     XButtonEvent button_event;         /* Button event packet. */
        !            65:     Bool func_stat;                    /* Function status return. */
        !            66:     int cur_x, cur_y;                  /* Current mouse position. */
        !            67:     Window sub_window;                 /* Current subwindow. */
        !            68:     int cur_item = 0;                  /* Current menu item. */
        !            69:     int hi_lite = 0;                   /* Current highlighted item. */
        !            70:     int i;                             /* Iteration counter. */
        !            71:     short hlfg, hlbg;                  /* Hi-liter pixels. */
        !            72:     MenuLine *ml;                      /* Menu lines pointer. */
        !            73:     char *hlname;                      /* Pointer to hi-liter name. */
        !            74:     char *strbuf;                      /* String buffer for IsTextNL. */
        !            75:     char *malloc();
        !            76: 
        !            77:     /*
        !            78:      * Change the cursor.
        !            79:      */
        !            80:     status = XGrabButton(RootWindow, MenuCursor, mask, EVENTMASK);
        !            81:     if (status == FAILURE)
        !            82:         Error("Menu -> Unable to grab button and change cursor.");
        !            83: 
        !            84:     /*
        !            85:      * Map the menu.
        !            86:      */
        !            87:     MapMenu(menu, x, y);
        !            88: 
        !            89:     /*
        !            90:      * Main loop.
        !            91:      */
        !            92:     while (TRUE) {
        !            93: 
        !            94:         /*
        !            95:          * If no button event, check the current mouse position.
        !            96:          */
        !            97:         status = XUpdateMouse(menu->w, &cur_x, &cur_y, &sub_window);
        !            98:         if (status == FAILURE) continue;
        !            99: 
        !           100:         /*
        !           101:          * If the mouse has moved out of the menu sideways, abort
        !           102:          * the menu operation. Reset the cursor and unmap the menu.
        !           103:          */
        !           104:         if (cur_x < 0 || cur_x > menu->width) {
        !           105:             UnmapMenu(menu, mask);
        !           106:             return(FALSE);
        !           107:         }
        !           108: 
        !           109:         /*
        !           110:          * If the mouse has moved below or above the menu, but is still
        !           111:          * within the same vertical plane, then simply adjust the values
        !           112:          * so the user doesn't fall off the edge.
        !           113:          */
        !           114:         if (cur_y >= menu->height) cur_y = menu->height - 1;
        !           115:         else if (cur_y < 0) cur_y = 0;
        !           116: 
        !           117:         /*
        !           118:          * If the mouse has moved to another item in the menu,
        !           119:          * highlight the new item.
        !           120:          */
        !           121:         cur_item = cur_y / menu->iheight;
        !           122:         if (cur_item != hi_lite) {
        !           123: 
        !           124:             /*
        !           125:              * Remove highlighting on old item.
        !           126:              */
        !           127:             if (hi_lite) {
        !           128:                 DisplayLine(menu->w, hi_lite * menu->iheight,
        !           129:                             menu->width, menu->iheight, hlname,
        !           130:                             hlfg, hlbg);
        !           131:             }
        !           132: 
        !           133:             /*
        !           134:              * Highlight new item.
        !           135:              */
        !           136:             if (cur_item) {
        !           137:                 for(i = 1, ml = menu->line; ml; i++, ml = ml->next) {
        !           138:                     if (i == cur_item) break;
        !           139:                 }
        !           140:                 DisplayLine(menu->w, cur_item * menu->iheight,
        !           141:                             menu->width, menu->iheight, ml->name,
        !           142:                             menu->hlfg.pixel, menu->hlbg.pixel);
        !           143:                 vlist[0].y = cur_item * menu->iheight + 1;
        !           144:                 XDraw(menu->w, vlist, NVERTS, 1, 1,
        !           145:                       menu->hlfg.pixel, GXcopy, AllPlanes);
        !           146:             }
        !           147:             hi_lite = cur_item;
        !           148:             hlfg = ml->fg.pixel;
        !           149:             hlbg = ml->bg.pixel;
        !           150:             hlname = ml->name;
        !           151:         }
        !           152: 
        !           153:         /*
        !           154:          * Check to see if we have a change in the mouse buttons.
        !           155:          * This means the user has selected an item or aborted the
        !           156:          * operation.
        !           157:          */
        !           158:         if (XPending() && GetButton(&button_event)) {
        !           159: 
        !           160:             /*
        !           161:              * Was button released?
        !           162:              */
        !           163:             if ((button_event.type == ButtonReleased) &&
        !           164:                 ((button_event.detail & ValueMask) == button)) {
        !           165:                 break;
        !           166:             } else {
        !           167: 
        !           168:                 /*
        !           169:                  * Some other button event occurred, so abort the menu
        !           170:                  * operation.
        !           171:                  */
        !           172:                 UnmapMenu(menu, mask);
        !           173:                 return(TRUE);
        !           174:             }
        !           175:         }
        !           176:     }
        !           177: 
        !           178:     /*
        !           179:      * If no item was selected, simply close the menu and return.
        !           180:      */
        !           181:     if (!cur_item) {
        !           182:         UnmapMenu(menu, mask);
        !           183:         return(TRUE);
        !           184:     }
        !           185: 
        !           186:     /*
        !           187:      * Get a pointer to the menu line selected.
        !           188:      */
        !           189:     --cur_item;
        !           190:     for(i = 0, ml = menu->line; ml; i++, ml = ml->next) {
        !           191:         if (i == cur_item) break;
        !           192:     }
        !           193: 
        !           194:     /*
        !           195:      * Perform the selected menu line action.
        !           196:      */
        !           197:     switch (ml->type) {
        !           198: 
        !           199:         case IsShellCommand:
        !           200:             UnmapMenu(menu, mask);
        !           201:             system(ml->text);
        !           202:             break;
        !           203: 
        !           204:         case IsText:
        !           205:             UnmapMenu(menu, mask);
        !           206:             XStoreBytes(ml->text, strlen(ml->text));
        !           207:             break;
        !           208: 
        !           209:         case IsTextNL:
        !           210:             UnmapMenu(menu, mask);
        !           211:             strbuf = (char *)malloc(strlen(ml->text) + 2);
        !           212:             strcpy(strbuf, ml->text);
        !           213:             strcat(strbuf, "\n");
        !           214:             XStoreBytes(strbuf, strlen(strbuf));
        !           215:             free(strbuf);
        !           216:             break;
        !           217: 
        !           218:         case IsUwmFunction:
        !           219:             GetContext(&sub_window, &cur_x, &cur_y);
        !           220:             UnmapMenu(menu, mask);
        !           221:             if (sub_window != menu->w)
        !           222:                 (*ml->func) (sub_window, mask, button, cur_x, cur_y);
        !           223:             break;
        !           224: 
        !           225:         case IsImmFunction:
        !           226:             UnmapMenu(menu, mask);
        !           227:             (*ml->func) (sub_window, mask, button, cur_x, cur_y);
        !           228:             break;
        !           229: 
        !           230:         case IsMenuFunction:
        !           231:             while (TRUE) {
        !           232:                 if (!GetButton(&button_event)) continue;
        !           233:                 if (button_event.type != ButtonPressed) continue;
        !           234:                 if ((KeyMask(button_event.detail) != KeyMask(mask)) ||
        !           235:                     ((button_event.detail & ButtonMods) != button)) {
        !           236:                     UnmapMenu(menu, mask);
        !           237:                     return(TRUE);
        !           238:                 }
        !           239:                 break;
        !           240:             }
        !           241:             UnmapMenu(menu, mask);
        !           242:             func_stat = Menu(menu->w, mask, button, x, y, ml->menu);
        !           243:             return(func_stat);
        !           244:             break;
        !           245: 
        !           246:         default:
        !           247:             Error("Menu -> Internal type error.");
        !           248:     }
        !           249:     return(TRUE);
        !           250: }
        !           251: 
        !           252: /*
        !           253:  * Create the menu windows for later use.
        !           254:  */
        !           255: CreateMenus()
        !           256: {
        !           257:     MenuLink *ptr;
        !           258: 
        !           259:     /*
        !           260:      * If MaxColors isn't set, then jam it to an impossibly high
        !           261:      * number.
        !           262:      */
        !           263:     if (MaxColors == 0)
        !           264:         MaxColors = 25000;
        !           265: 
        !           266:     for(ptr = Menus; ptr; ptr = ptr->next)
        !           267:         InitMenu(ptr->menu);
        !           268: }
        !           269: 
        !           270: /*
        !           271:  * Initialize a menu.
        !           272:  */
        !           273: InitMenu(menu)
        !           274: MenuInfo *menu;
        !           275: {
        !           276:     MenuLine *ml;              /* Menu lines pointer. */
        !           277:     int width;                 /* Width of an item name. */
        !           278:     int maxwidth;              /* Maximum width of item names. */
        !           279:     int len;                   /* Length of an item name. */
        !           280:     int count = 1;             /* Number of items + 1 for name. */
        !           281: 
        !           282:     /*
        !           283:      * Determine the name of the longest menu item.
        !           284:      */
        !           285:     maxwidth = XQueryWidth(menu->name, MFont);
        !           286:     if (maxwidth == 0)
        !           287:         Error("InitMenu -> Couldn't get length of menu name");
        !           288: 
        !           289:     for(ml = menu->line; ml; ml = ml->next) {
        !           290:         if ((len = strlen(ml->name)) == 0)
        !           291:             break;
        !           292:         width = XQueryWidth(ml->name, MFont);
        !           293:         if (width == 0) Error("InitMenu -> Couldn't get length of menu item name");
        !           294:         if (width > maxwidth) maxwidth = width;
        !           295:         count++;
        !           296:     }
        !           297: 
        !           298:     /*
        !           299:      * Get the color cells for the menu items.
        !           300:      */
        !           301:     GetMenuColors(menu);
        !           302: 
        !           303:     /*
        !           304:      * Stash the menu parameters in the menu info structure.
        !           305:      */
        !           306:     menu->iheight = MFontInfo.height + (VMenuPad << 1);
        !           307:     menu->height = menu->iheight * count;
        !           308:     menu->width = maxwidth + (HMenuPad << 1);
        !           309:     menu->image = NULL;
        !           310: 
        !           311:     /*
        !           312:      * Create the menu window.
        !           313:      */
        !           314:     menu->w = XCreateWindow(RootWindow,
        !           315:                             0, 0,
        !           316:                             menu->width,
        !           317:                             menu->height,
        !           318:                             MBorderWidth,
        !           319:                             MBorder, MBackground);
        !           320:     if (menu->w == NULL) Error("InitMenu -> Couldn't create menu window");
        !           321: 
        !           322:     /*
        !           323:      * Store the window name.
        !           324:      */
        !           325:     XStoreName(menu->w, menu->name);
        !           326: 
        !           327:     /*
        !           328:      * Define a cursor for the window.
        !           329:      */
        !           330:     XDefineCursor(menu->w, MenuCursor);
        !           331: }
        !           332: 
        !           333: /*
        !           334:  * Map a menu.
        !           335:  */
        !           336: MapMenu(menu, x, y)
        !           337: MenuInfo *menu;
        !           338: int x, y;
        !           339: {
        !           340:     int item;
        !           341:     Window w;
        !           342:     MenuLine *ml;
        !           343: 
        !           344:     w = menu->w;
        !           345: 
        !           346:     /*
        !           347:      * Move the menu into place, normalizing the coordinates, if necessary;
        !           348:      * then map it.
        !           349:      */
        !           350:     x -= (menu->width >> 1);
        !           351:     if (x < 0) x = 0;
        !           352:     else if (x + menu->width >= ScreenWidth)
        !           353:         x = ScreenWidth - menu->width - (MBorderWidth << 1);
        !           354:     if (y < 0) y = 0;
        !           355:     else if (y + menu->height >= ScreenHeight)
        !           356:         y = ScreenHeight - menu->height - (MBorderWidth << 1);
        !           357:     XMoveWindow(w, x, y);
        !           358: 
        !           359:     /*
        !           360:      * Map the window and draw the text items.
        !           361:      */
        !           362:     XMapWindow(w);
        !           363:     DisplayLine(w, 0, menu->width, menu->iheight, menu->name,
        !           364:                 menu->bg.pixel, menu->fg.pixel);
        !           365: 
        !           366:     SetUpVlist(menu);
        !           367:     vlist[0].x = 1;
        !           368:     vlist[0].y = 1;
        !           369:     XDraw(menu->w, vlist, NVERTS, 1, 1, menu->bg.pixel, GXcopy, AllPlanes);
        !           370:     item = menu->iheight;
        !           371:     for(ml = menu->line; ml; ml = ml->next) {
        !           372:         DisplayLine(w, item, menu->width, menu->iheight, ml->name,
        !           373:                     ml->fg.pixel, ml->bg.pixel);
        !           374:         item += menu->iheight;
        !           375:     }
        !           376: 
        !           377:     /*
        !           378:      * Position the mouse cursor in the menu header (or in the first item
        !           379:      * if "autoselect" is set).
        !           380:      */
        !           381:     if (Autoselect)
        !           382:         XWarpMouse(w, (menu->width >> 2) * 3, (menu->iheight >> 1) * 3);
        !           383:     else XWarpMouse(w, (menu->width >> 2) * 3, menu->iheight >> 1);
        !           384: 
        !           385:     XFlush();
        !           386: }
        !           387: 
        !           388: /*
        !           389:  * Unmap a menu, restoring the contents of the screen underneath
        !           390:  * if necessary. (Restore portion is a future.)
        !           391:  */
        !           392: UnmapMenu(menu, mask)
        !           393: MenuInfo *menu;
        !           394: int mask;
        !           395: {
        !           396:     /*
        !           397:      * Restore the main cursor.
        !           398:      */
        !           399:     Grab((short)mask);
        !           400: 
        !           401:     /*
        !           402:      * Unmap and flush.
        !           403:      */
        !           404:     XUnmapWindow(menu->w);
        !           405:     XFlush();
        !           406: }
        !           407: 
        !           408: /*
        !           409:  * Get the context for invoking a window manager function.
        !           410:  */
        !           411: GetContext(w, x, y)
        !           412: Window *w;
        !           413: int *x, *y;
        !           414: {
        !           415:     XButtonEvent button_event;  /* Button input event. */
        !           416: 
        !           417:     while (TRUE) {
        !           418: 
        !           419:         /*
        !           420:          * Get the next mouse button event.  Spin our wheels until
        !           421:          * a button event is returned (ie. GetButton == TRUE).
        !           422:          * Note that mouse events within an icon window are handled
        !           423:          * in the "GetButton" function or by the icon's owner if
        !           424:          * it is not uwm.
        !           425:          */
        !           426:         if (!GetButton(&button_event)) continue;
        !           427: 
        !           428:         /*
        !           429:          * If the button event received is not a ButtonPressed event
        !           430:          * then continue until we find one.
        !           431:          */
        !           432:         if (button_event.type != ButtonPressed) continue;
        !           433: 
        !           434:         /*
        !           435:          * Okay, determine the event window and mouse coordinates.
        !           436:          */
        !           437:         status = XInterpretLocator(RootWindow,
        !           438:                                     x, y,
        !           439:                                     w,
        !           440:                                     button_event.location);
        !           441: 
        !           442:         if (status == FAILURE) continue;
        !           443: 
        !           444:         if (*w == 0)
        !           445:             *w = RootWindow;
        !           446: 
        !           447:         return;
        !           448:     }
        !           449: }
        !           450: 
        !           451: /*
        !           452:  * Get the color cells for a menu.  This function is slightly brain-damaged
        !           453:  * in that once MaxColors <= 1, then it refuses to even try to allocate any
        !           454:  * more colors, even though the colors may have already been allocated.  It
        !           455:  * probably ought to be done right someday.
        !           456:  */
        !           457: GetMenuColors(menu)
        !           458: MenuInfo *menu;
        !           459: {
        !           460:     register MenuLine *ml;             /* Menu lines pointer. */
        !           461: 
        !           462:     /*
        !           463:      * If we have more than 2 colors available, then attempt to get
        !           464:      * the color map entries requested by the user.
        !           465:      * Otherwise, default to standard black and white.
        !           466:      */
        !           467:     if (DisplayCells() > 2) {
        !           468: 
        !           469:         /*
        !           470:          * Get the menu header colors first.
        !           471:          */
        !           472:         if (!(menu->foreground && menu->background && MaxColors > 1 &&
        !           473:               XParseColor(menu->foreground, &menu->fg) &&
        !           474:               XGetHardwareColor(&menu->fg) &&
        !           475:               XParseColor(menu->background, &menu->bg) &&
        !           476:               XGetHardwareColor(&menu->bg))) {
        !           477:             menu->fg.pixel = MTextForground;
        !           478:             menu->bg.pixel = MTextBackground;
        !           479:         } else {
        !           480:             AdjustMaxColors(menu->fg.pixel);
        !           481:             AdjustMaxColors(menu->bg.pixel);
        !           482:         }
        !           483: 
        !           484:         /*
        !           485:          * Get the menu highlight colors.
        !           486:          */
        !           487:         if (!(menu->fghighlight && menu->bghighlight && MaxColors > 1 &&
        !           488:               XParseColor(menu->fghighlight, &menu->hlfg) &&
        !           489:               XGetHardwareColor(&menu->hlfg) &&
        !           490:               XParseColor(menu->bghighlight, &menu->hlbg) &&
        !           491:               XGetHardwareColor(&menu->hlbg))) {
        !           492:             menu->hlfg.pixel = MTextBackground;
        !           493:             menu->hlbg.pixel = MTextForground;
        !           494:         } else {
        !           495:             AdjustMaxColors(menu->hlfg.pixel);
        !           496:             AdjustMaxColors(menu->hlbg.pixel);
        !           497:         }
        !           498: 
        !           499:         /*
        !           500:          * Get the menu item colors.
        !           501:          */
        !           502:         for(ml = menu->line; ml; ml = ml->next) {
        !           503:             if (!(ml->foreground && ml->background && MaxColors > 1 &&
        !           504:                   XParseColor(ml->foreground, &ml->fg) &&
        !           505:                   XGetHardwareColor(&ml->fg) &&
        !           506:                   XParseColor(ml->background, &ml->bg) &&
        !           507:                   XGetHardwareColor(&ml->bg))) {
        !           508:                 ml->fg.pixel = MTextForground;
        !           509:                 ml->bg.pixel = MTextBackground;
        !           510:             } else {
        !           511:                 AdjustMaxColors(ml->fg.pixel);
        !           512:                 AdjustMaxColors(ml->bg.pixel);
        !           513:             }
        !           514:         }
        !           515: 
        !           516:     } else {
        !           517: 
        !           518:         /*
        !           519:          * Only 2 colors available, so default to standard black and white.
        !           520:          */
        !           521:         menu->fg.pixel = MTextForground;
        !           522:         menu->bg.pixel = MTextBackground;
        !           523:         menu->hlfg.pixel = MTextBackground;
        !           524:         menu->hlbg.pixel = MTextForground;
        !           525:         for(ml = menu->line; ml; ml = ml->next) {
        !           526:             ml->fg.pixel = MTextForground;
        !           527:             ml->bg.pixel = MTextBackground;
        !           528:         }
        !           529:     }
        !           530: }
        !           531: 
        !           532: /*
        !           533:  * Decrement "MaxColors" if this pixel value has never been used in a
        !           534:  * menu before.
        !           535:  */
        !           536: AdjustMaxColors(pixel)
        !           537: int pixel;
        !           538: {
        !           539:     register MenuLink *mptr;
        !           540:     register MenuLine *lptr;
        !           541:     int count = 0;
        !           542: 
        !           543:     for(mptr = Menus; mptr; mptr = mptr->next) {
        !           544:         if (mptr->menu->fg.pixel == pixel) ++count;
        !           545:         if (mptr->menu->bg.pixel == pixel) ++count;
        !           546:         if (mptr->menu->hlfg.pixel == pixel) ++count;
        !           547:         if (mptr->menu->hlbg.pixel == pixel) ++count;
        !           548:         for(lptr = mptr->menu->line; lptr; lptr = lptr->next) {
        !           549:             if (lptr->fg.pixel == pixel) ++count;
        !           550:             if (lptr->bg.pixel == pixel) ++count;
        !           551:         }
        !           552:         if (count > 1) return;
        !           553:     }
        !           554:     --MaxColors;
        !           555: }
        !           556: 
        !           557: /*
        !           558:  * Set up the vertex list for the hi-liter.
        !           559:  */
        !           560: SetUpVlist(menu)
        !           561: MenuInfo *menu;
        !           562: {
        !           563:     vlist[1].x = menu->width - 3;
        !           564:     vlist[1].y = 0;
        !           565:     vlist[2].x = 0;
        !           566:     vlist[2].y = menu->iheight - 3;
        !           567:     vlist[3].x = (short)(0 - menu->width + 3);
        !           568:     vlist[3].y = 0;
        !           569:     vlist[4].x = 0;
        !           570:     vlist[4].y = (short)(0 - menu->iheight + 3);
        !           571:     vlist[1].flags = vlist[2].flags = vlist[3].flags =
        !           572:     vlist[4].flags = VertexRelative;
        !           573: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.