home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / x / volume19 / xodometer / part04 / xodo.c
Encoding:
C/C++ Source or Header  |  1993-04-28  |  44.7 KB  |  1,312 lines

  1. /*
  2.  
  3.   xodometer - Track the total distance your pointing device and cursor
  4.               travel.  The distance can be displayed in various units.
  5.  
  6.   Inspired by the MacIntosh Mouse Odometer by Sean P. Nolan.          
  7.  
  8.   Template code from xneko by Masayuki Koba.
  9.  
  10.   For X11 Release 4.
  11.  
  12.   Stephen O. Lidie, 93/01/20.  lusol@Lehigh.EDU
  13.  
  14.  
  15.   Given the number of pixels and the screen dimensions in millimeters
  16.   we use this distance formula:
  17.  
  18.   distance = sqrt( (dX * (Xmm/Xpixels))**2 + (dY * (Ymm/Ypixels))**2 )
  19.  
  20.   Where dX and dY are pixel differentials, and Xmm, Ymm and Xpixels,
  21.   Ypixels are the screen dimensions in millimeters and pixels,
  22.   respectively.
  23.  
  24.   My first X application and my first graphical application too.  Yes,
  25.   I used Xlib for the experience... never again though!
  26.   
  27. */
  28.  
  29.  
  30. #include <X11/Xlib.h>
  31. #include <X11/Xutil.h>
  32. #include <X11/Xresource.h>
  33.  
  34. #include <stdio.h>
  35. #include <signal.h>
  36. #include <math.h>
  37. #include <sys/time.h>
  38. #include <sys/errno.h>
  39.  
  40. #include "evap/evap.h"
  41. #include "evap/xodo_pdt.out"
  42.  
  43. #include "patchlevel.h"
  44.  
  45. #include "bitmaps/icon.xbm"
  46. #include "bitmaps/cursor.xbm"
  47. #include "bitmaps/cur_mask.xbm"
  48. #include "bitmaps/pulldown.xbm"
  49.  
  50. #define DEBUG           0    /* 1 IFF debug */
  51.  
  52. #define CURSOR_S        "Cursor"
  53. #define POINTER_S       "Pointer"
  54.  
  55. /*
  56.   EVENT_MASK1 - Top level window.
  57.   EVENT_MASK2 - Action windows like About, Quit, Units and trip odometer reset buttons.
  58.   EVENT_MASK3 - Menu pane windows.
  59. */
  60.  
  61. #define    EVENT_MASK1    ButtonPressMask | ButtonReleaseMask | ExposureMask | StructureNotifyMask
  62. #define EVENT_MASK2    ButtonPressMask | ButtonReleaseMask | OwnerGrabButtonMask | EnterWindowMask | LeaveWindowMask
  63. #define EVENT_MASK3     ButtonPressMask | ButtonReleaseMask | ExposureMask | EnterWindowMask | LeaveWindowMask
  64.  
  65. /* The ALL_EVENTS_MASK mask is a superset of all possible events that ANY window might need. */
  66.  
  67. #define ALL_EVENTS_MASK EVENT_MASK1 | EVENT_MASK2 | EVENT_MASK3
  68.  
  69. /* Action window ordinals. */
  70.  
  71. #define TRIP1           0
  72. #define TRIP2           1
  73. #define QUIT            2
  74. #define UNITS           3
  75. #define ABOUT           4
  76.  
  77. /* Odometer ordinals. */
  78.  
  79. #define CURSOR          0
  80. #define POINTER         1
  81. #define BOTH            2
  82.  
  83. /* Function prototypes, sorta. */
  84.  
  85. void                    compute_font_and_resize_data();
  86. void                    draw_action_windows();
  87. void                    draw_menu();
  88. void                    draw_menu_panes();
  89. void                    draw_misc_info();
  90. void                    draw_odometers();
  91. void                    draw_odometer_digit();
  92. int                     evap();
  93. void                    evap_type_conversion();
  94. void                    finish_xodo();
  95. void                    highlight_action_window();
  96. void                    initialize_graphics_contexts();
  97. void                    initialize_menu();
  98. void                    initialize_xodo();
  99. Bool                    process_event();
  100. void                    process_pointer();
  101. void                    save_xodo();
  102.  
  103. /* Global variables, more or less alphabetically. */
  104.  
  105. char                    *about[] = {
  106.                           "         xodometer xxx",
  107.                           " ",
  108.                           "The Mac Mouse Odometer, X Style ",
  109.                           "   Stephen O. Lidie, 93/02/11",
  110.                           "       lusol@Lehigh.EDU",
  111.               " ",
  112.               "   For help:  xodo -full_help",
  113.             };
  114. short                   about_active = False;
  115. int                     about_count = sizeof( about ) / sizeof( *about );
  116. int                     about_width, about_height;
  117. int                     about_width_old, about_height_old, about_x_old, about_y_old;
  118. int                     about_width_x;
  119. int                     accel_numerator, accel_denominator;
  120. double                  acceleration;
  121. int                     ascent;
  122. float                   aspect;
  123. Atom                    atom_wm_save_yourself;
  124. int                     autosave_count, autosave_ticks;
  125. unsigned int        BorderWidth = 2;
  126. short                   button_depressed = False;
  127. char                    calibration[] = "2.54 cm/in";
  128. int                     calibration_width;
  129. int                     coordinates_x, coordinates_y;
  130.  
  131. #if DEBUG
  132. FILE                    *d;
  133. #endif
  134.  
  135. int                     descent;
  136. int                     direction;
  137. unsigned int            display_width, display_height;
  138. unsigned int            display_widthmm, display_heightmm;
  139. double                  distance;
  140. struct distances {
  141.   char *name;
  142.   char *abbreviation;
  143.   Window menu_pane;
  144.   double value;
  145. }                       distances[] = {
  146.                           {"millimeters",        "mm ", None, 1.0},
  147.                           {"centimeters",        "cm ", None, 0.1},
  148.                           {"decimeters",         "dm ", None, 0.01},
  149.                           {"meters",             "m  ", None, 0.001},
  150.                           {"dekameters",         "dam", None, 0.0001},
  151.                           {"hectometers",        "hm ", None, 0.00001},
  152.                           {"kilometers",         "km ", None, 0.000001},
  153.                           {"myriameters",        "mym", None, 0.0000001},
  154.                           {"inches",             "in ", None, 0.1/2.54},
  155.                           {"feet",               "ft ", None, 0.1/2.54/12.0},
  156.                           {"yards",              "yd ", None, 0.1/2.54/12.0/3.0},
  157.                           {"rods",               "rd ", None, 0.1/2.54/12.0/3.0/5.5},
  158.                           {"miles",              "mi ", None, 0.1/2.54/12.0/3.0/1760.0},
  159.                           {"furlongs",           "fl ", None, 0.1/2.54/12.0/3.0/220.0},
  160.                           {"fathoms",            "fm ", None, 0.1/2.54/12.0/6.0},
  161.                           {"light-nanoseconds",  "lns", None, 0.001/299792458.0*1.0E+9},
  162.               {"marine_leagues",     "mlg", None, 0.001/1852.0/3.0},
  163.               {"nautical_miles",     "nm ", None, 0.001/1852.0},
  164.                         };
  165. char                    distances_human[] = "U=lns";
  166. int                     distances_ordinal = 0;
  167. extern                  errno;
  168. XFontStruct             *font_info;
  169. XFontStruct             *font_info2;
  170. char                    *fontname;
  171. int                     font_width, font_height;
  172. GC                      gc;
  173. GC                      gc2;
  174. GC                      gc3;
  175. GC                      gc_reverse;
  176. GC                      gc2_reverse;
  177. int                     label_cursor_x, label_cursor_y;
  178. int                     label_pointer_x, label_pointer_y;
  179. short                   menu_active = False;
  180. int                     menu_border_width = 4;
  181. int                     menu_pane_count = sizeof( distances) / sizeof( struct distances );
  182. int                     menu_pane_height;
  183. int                     menu_width, menu_height;
  184. int                     menu_width_old, menu_height_old, menu_x_old, menu_y_old;
  185. unsigned long           motion_buffer_size;
  186. int                     odometer_count = BOTH;
  187. FILE                    *OF;
  188. char                    old_line1[20] = "!!!!!!!!!!!!!!!!!!!";
  189. char                    old_line2[20] = "!!!!!!!!!!!!!!!!!!!";
  190. char                    old_line3[20] = "!!!!!!!!!!!!!!!!!!!";
  191. char                    old_line4[20] = "!!!!!!!!!!!!!!!!!!!";
  192. XCharStruct             overall;
  193. int                     pixels_per_cm_x, pixels_per_cm_y;
  194. int                     pixels_per_inch_x, pixels_per_inch_y;
  195. int            PointerX;
  196. int            PointerY;
  197. int            PrevPointerX = 0;
  198. int            PrevPointerY = 0;
  199. char            *ProgramName;
  200. Pixmap                  pulldown;
  201. XRectangle              rectangles[10] = {   3, 25, 20, 20,
  202.                                             23, 25, 20, 20,
  203.                         43, 25, 20, 20,
  204.                         63, 25, 20, 20,
  205.                         83, 25, 20, 20,
  206.                        103, 25, 20, 20,
  207.                        123, 25, 20, 20,
  208.                        143, 25, 20, 20,
  209.                        163, 25, 20, 20,
  210.                        183, 25, 20, 20,
  211.                                          };
  212. XRectangle              rectangles2[10];
  213. XRectangle              rectangles3[10];
  214. XRectangle              rectangles4[10];
  215. Status                  status;
  216. XColor            theBackgroundColor;
  217. unsigned long        theBackgroundPixel;
  218. XColor                  theBorderColor;
  219. unsigned long           theBorderPixel;
  220. Cursor                  theCursor;
  221. unsigned int        theDepth;
  222. Display            *theDisplay;
  223. XColor            theExactColor;
  224. XColor            theForegroundColor;
  225. unsigned long        theForegroundPixel;
  226. Pixmap            theIconPixmap;
  227. Window                  theMenu;
  228. Window            theRoot;
  229. int            theScreen;
  230. Window            theWindow;
  231. int                     threshold;
  232. double                  total_cursor_distance, trip_cursor_distance; /* distances in millimeters */
  233. double                  total_pointer_distance, trip_pointer_distance; /* distances in millimeters */
  234. unsigned long           valuemask;
  235. XGCValues               values;
  236. struct windata{
  237.   Window window;
  238.   int color;
  239.   char *text;
  240.   int x;
  241.   int y;
  242.   int width;
  243.   int height;
  244.   int border;
  245. }                       windata[] = {
  246.                           {None, 0, "",          0, 0,  8,  3, 2},
  247.                           {None, 0, "",          0, 0,  8,  3, 2},
  248.                           {None, 0, "Quit",     40, 2, 25, 10, 2},
  249.                           {None, 0, "  Units", 205, 2, 45, 10, 2},
  250.                           {None, 0, "About",     2, 2, 30, 10, 2}, 
  251.                         };
  252. int                     windata_count = sizeof( windata) / sizeof( struct windata );
  253. int                     window_width = 1;
  254. int                 window_height = 1;
  255. unsigned int        WindowHeight = 1;
  256. int              WindowPointX = 1;
  257. int              WindowPointY = 1;
  258. unsigned int        WindowWidth = 1;
  259. double                  X_mm_per_pixel, Y_mm_per_pixel;
  260. char                    status_line[100];
  261.  
  262.  
  263.  
  264.  
  265. /* Global procedures; again, alphabetically. */
  266.  
  267.  
  268.  
  269.  
  270. void
  271. compute_font_and_resize_data( font_info ) /* size rectangles et. al. based on font */
  272.      XFontStruct                *font_info;
  273. {
  274.  
  275.   int                           i, width_incr;
  276.  
  277.   if ( font_info != NULL ) {
  278.  
  279.     font_width = font_info->max_bounds.rbearing - font_info->min_bounds.lbearing;
  280.     font_height = font_info->max_bounds.ascent + font_info->max_bounds.descent;
  281.  
  282.     for ( i = 0; i < 10; i++ ) {
  283.       rectangles[i].height = font_height + 5;
  284.       rectangles[i].width = rectangles[i].height;
  285.       if ( i > 0 )
  286.     rectangles[i].x = rectangles[i-1].x + font_height + 5;
  287.       if ( font_height + 5 > 25 )
  288.     rectangles[i].y = font_height + 5;
  289.     }
  290.  
  291.   }
  292.  
  293.   width_incr = (int)( aspect * (float)rectangles[0].width - (float)rectangles[0].width );
  294.  
  295.   for( i = 0; i < 10; i++ ){
  296.     if ( i >= 5 )
  297.       rectangles[i].height += 1;
  298.     rectangles[i].width += width_incr;
  299.     rectangles[i].x = rectangles[i].x + (i * width_incr);
  300.     
  301.     rectangles2[i] = rectangles[i];
  302.     rectangles2[i].y = rectangles[i].y + (font_height * 2);
  303.     
  304.     rectangles3[i] = rectangles[i];
  305.     rectangles3[i].y = rectangles[i].y + (font_height * 5);
  306.     
  307.     rectangles4[i] = rectangles[i];
  308.     rectangles4[i].y = rectangles[i].y + (font_height * 7);
  309.   }
  310.   
  311.   windata[TRIP1].x = rectangles2[0].x;
  312.   windata[TRIP1].y = rectangles2[0].y - 7;
  313.   windata[TRIP2].x = rectangles4[0].x;
  314.   windata[TRIP2].y = rectangles4[0].y - 7;
  315.   
  316.   window_width = rectangles[9].x + rectangles[9].width + 3;
  317.  
  318.   i = font_info2->max_bounds.ascent + font_info2->max_bounds.descent;
  319.   if ( font_height > i )
  320.     window_height = rectangles4[0].y + 7 + (font_height * 2);
  321.   else
  322.     window_height = rectangles4[0].y + 7 + (i * 2);
  323.   if ( odometer_count != BOTH )
  324.     window_height = window_height + rectangles2[0].y - rectangles4[0].y;
  325.  
  326.   about_width_x = window_width;
  327.  
  328.   XTextExtents( font_info, CURSOR_S, strlen( CURSOR_S ), &direction, &ascent, &descent, &overall );
  329.   label_cursor_x = ( window_width - overall.width ) / 2;
  330.   label_cursor_y = rectangles[0].y - (font_height/2) + font_info->max_bounds.descent;
  331.   XTextExtents( font_info, POINTER_S, strlen( POINTER_S ), &direction, &ascent, &descent, &overall );
  332.   label_pointer_x = ( window_width - overall.width ) / 2;
  333.   label_pointer_y = rectangles3[0].y - (font_height/2) + font_info->max_bounds.descent;
  334.  
  335.   if ( font_info != NULL ) {
  336.     if ( (WindowWidth != window_width) || (WindowHeight != window_height) ) {
  337.       WindowWidth = window_width;
  338.       WindowHeight = window_height;
  339.       XMoveWindow( theDisplay, windata[TRIP1].window, windata[TRIP1].x, windata[TRIP1].y );
  340.       if ( odometer_count == BOTH )
  341.     XMoveWindow( theDisplay, windata[TRIP2].window, windata[TRIP2].x, windata[TRIP2].y );
  342.       XMoveWindow( theDisplay, windata[UNITS].window, window_width-50, windata[UNITS].y );
  343.       XResizeWindow( theDisplay, theWindow, WindowWidth, WindowHeight );
  344.     }
  345.   } /* ifend font_info != NULL */
  346.  
  347.   coordinates_x = 1;        /* compute Units and absolute coordinates display postion */
  348.   coordinates_y = rectangles4[0].y + font_height + (font_height / 2) + font_info2->max_bounds.ascent + 2;
  349.   if ( odometer_count != BOTH ) {
  350.     coordinates_y += (rectangles2[0].y - rectangles4[0].y);
  351.   }
  352.  
  353. } /* end compute_font_and_resize_data */
  354.  
  355.  
  356.  
  357.  
  358. void
  359. do_distances( refresh_digits )    /* compute and display the distances */
  360.      short                      refresh_digits;
  361. {
  362.  
  363.     Window               QueryRoot, QueryChild;
  364.     int                    AbsoluteX, AbsoluteY;
  365.     int                    RelativeX, RelativeY;
  366.     unsigned int            ModKeyMask;
  367.     double                dXmm, dYmm;
  368.     int                         dX, dY;
  369.     char                        line1[20], line2[20], line3[20], line4[20];
  370.     double                      units;
  371.     int                         i, n, x, y;
  372.     char                        coordinates[13]; /* allows for a 99,999 by 99,999 display */
  373.     int                         coord_len;
  374.  
  375.     if ( refresh_digits ) {
  376.       strcpy( old_line1, "!!!!!!!!!!!!!!!!!!!" );
  377.       strcpy( old_line2, "!!!!!!!!!!!!!!!!!!!" );
  378.       strcpy( old_line3, "!!!!!!!!!!!!!!!!!!!" );
  379.       strcpy( old_line4, "!!!!!!!!!!!!!!!!!!!" );
  380.     }
  381.  
  382.     XQueryPointer( theDisplay, theWindow, &QueryRoot, &QueryChild, &AbsoluteX, &AbsoluteY, &RelativeX, &RelativeY, &ModKeyMask );
  383.  
  384.     PrevPointerX = PointerX;
  385.     PrevPointerY = PointerY;
  386.  
  387.     PointerX = AbsoluteX;
  388.     PointerY = AbsoluteY;
  389.  
  390.     dX = PointerX - PrevPointerX;
  391.     dY = PointerY - PrevPointerY;
  392.  
  393.     if ( dX < 0.0 )
  394.       dX = -dX;
  395.     if ( dY < 0.0 )
  396.       dY = -dY;
  397.     
  398.     dXmm = (double)dX * X_mm_per_pixel;
  399.     dYmm = (double)dY * Y_mm_per_pixel;
  400.  
  401.     distance = sqrt( (dXmm * dXmm) + (dYmm * dYmm) );
  402.  
  403.     if ( distance != 0 ) {
  404.  
  405.       trip_cursor_distance += distance;
  406.       total_cursor_distance += distance;
  407.       if ( dX > threshold || dY > threshold ) {
  408.     distance /= acceleration; /* if cursor was accelerated, we suppose! */
  409.       }
  410.       distance /= pvt[P_POINTER_SCALE_FACTOR].value.real_value;
  411.       trip_pointer_distance += distance;
  412.       total_pointer_distance += distance;
  413.     }
  414.  
  415.     units = distances[distances_ordinal].value;
  416.  
  417.     if ( odometer_count == CURSOR || odometer_count == BOTH ) {
  418.       sprintf( line1, "%011.5f", fmod( (total_cursor_distance * units), 100000.0 ) );
  419.       sprintf( line2, "%011.5f", fmod( (trip_cursor_distance * units), 100000.0 ) );
  420.     } else if ( odometer_count == POINTER ) {
  421.       sprintf( line1, "%011.5f", fmod( (total_pointer_distance * units), 100000.0 ) );
  422.       sprintf( line2, "%011.5f", fmod( (trip_pointer_distance * units), 100000.0 ) );
  423.     }
  424.     if ( odometer_count == BOTH ) {
  425.       sprintf( line3, "%011.5f", fmod( (total_pointer_distance * units), 100000.0 ) );
  426.       sprintf( line4, "%011.5f", fmod( (trip_pointer_distance * units), 100000.0 ) );
  427.     }
  428.  
  429.     for(i = 0, n = 0; i < 11; i++) {
  430.       if ( i == 5 ) {
  431.     continue;        /* skip units point */
  432.       } else if ( i < 5 ) {    /* draw digits to the left of the point */
  433.     draw_odometer_digit( gc, rectangles[n], line1+i, old_line1+i );
  434.     draw_odometer_digit( gc, rectangles2[n], line2+i, old_line2+i );
  435.     if ( odometer_count == BOTH ) {
  436.       draw_odometer_digit( gc, rectangles3[n], line3+i, old_line3+i );
  437.       draw_odometer_digit( gc, rectangles4[n], line4+i, old_line4+i );
  438.     }
  439.       } else {            /* draw digits to the right of the point */
  440.     draw_odometer_digit( gc_reverse, rectangles[n], line1+i, old_line1+i );
  441.     draw_odometer_digit( gc_reverse, rectangles2[n], line2+i, old_line2+i );
  442.     if ( odometer_count == BOTH ) {
  443.       draw_odometer_digit( gc_reverse, rectangles3[n], line3+i, old_line3+i );
  444.       draw_odometer_digit( gc_reverse, rectangles4[n], line4+i, old_line4+i );
  445.     }
  446.       }
  447.       n++;            /* advance to next rectangle */
  448.     }
  449.  
  450.     sprintf( coordinates, "(%d,%d)", PointerX, PointerY ); /* draw absolute pointer coordinates */
  451.                                                                        /* Einstein: 'You said "absolute"?' */
  452.     coord_len = strlen( coordinates );
  453.     strncpy( coordinates+coord_len, "             ", 13-coord_len );
  454.     XDrawImageString( theDisplay, theWindow, gc2, coordinates_x+33, coordinates_y, coordinates, 13 );
  455.  
  456. } /* end do_distances */
  457.  
  458.  
  459.  
  460.  
  461. void
  462. draw_action_windows( gc )
  463.      GC                gc;
  464. {
  465.  
  466.   int                           i;
  467.  
  468.   for ( i = 0; i < windata_count; i++ ) {
  469.     if ( odometer_count != BOTH && i == TRIP2 )
  470.       continue;            /* skip second trip odometer if only 1 odometer */
  471.     XDrawImageString( theDisplay, windata[i].window, gc, font_info2->max_bounds.lbearing-3, font_info2->max_bounds.ascent+1,
  472.           windata[i].text, strlen(windata[i].text) );
  473.   } /* forend */
  474.   XCopyPlane( theDisplay, pulldown, windata[UNITS].window, gc, 0, 0, pulldown_width, pulldown_height, 1, 1, 1 );
  475.   
  476. } /* end draw_action_windows */
  477.  
  478.  
  479.  
  480.  
  481. void
  482. draw_menu()
  483. {
  484.  
  485.   int                new_width, new_height;
  486.  
  487.   menu_active = True;
  488.   XMapWindow( theDisplay, theMenu );
  489.   menu_width_old = WindowWidth;
  490.   menu_height_old = WindowHeight;
  491.   if ( WindowWidth < menu_width + font_info->max_bounds.width ) 
  492.     new_width = menu_width + font_info->max_bounds.width;
  493.   else
  494.     new_width = WindowWidth;
  495.   if ( WindowHeight < menu_height + menu_pane_height ) 
  496.     new_height = menu_height + menu_pane_height;
  497.   else
  498.     new_height = WindowHeight;
  499.   menu_x_old = WindowPointX;
  500.   menu_y_old = WindowPointY;
  501.   if ( WindowPointY + new_height > display_height ) {
  502.     menu_y_old = display_height - new_height;
  503.   }
  504.  
  505.   XMoveResizeWindow( theDisplay, theWindow, menu_x_old, menu_y_old, new_width, new_height );
  506.   menu_y_old = WindowPointY;
  507.   XGrabPointer( theDisplay, theMenu, True, EVENT_MASK2, GrabModeAsync, GrabModeAsync, None, theCursor, CurrentTime );
  508.  
  509. } /* end draw_menu */
  510.  
  511.  
  512.  
  513.  
  514. void
  515. draw_menu_panes( gc, window )
  516.      GC                gc;
  517.      Window                     window;
  518. {
  519.  
  520.   int                           i, j;
  521.   char                          units_name[100];
  522.  
  523.   for ( i = 0; i < menu_pane_count; i++ ) {
  524.     if ( window  == distances[i].menu_pane ) {
  525.       strcpy( units_name, distances[i].name );
  526.       for ( j = 0; j < strlen( units_name ); j++ )
  527.     if ( units_name[j] == '_' )
  528.       units_name[j] = ' ';
  529.       XDrawImageString( theDisplay, distances[i].menu_pane, gc, 1, font_info->max_bounds.ascent, units_name,
  530.             strlen( units_name ) );
  531.     } /* ifend */
  532.   } /* forend */
  533.   
  534. } /* end draw_menu_panes */
  535.  
  536.  
  537.  
  538.  
  539. void
  540. draw_misc_info()
  541. {
  542.  
  543.   int                           x, y;
  544.   int                           i, n;
  545.  
  546.   /* Draw the Units. */
  547.  
  548.   XDrawImageString( theDisplay, theWindow, gc2, coordinates_x, coordinates_y, distances_human, strlen( distances_human ) );
  549.   
  550.   /* Draw the About information in a (normally) hidden part of the window. */
  551.   
  552.   for ( i = 0, n = font_height; i < about_count; i++, n += font_height ) {
  553.     XDrawImageString( theDisplay, theWindow, gc, about_width_x, n, about[i], strlen( about[i] ) );
  554.   }
  555.   XCopyPlane( theDisplay, theIconPixmap, theWindow, gc, 0, 0, icon_width, icon_height, about_width_x,
  556.          about_height-icon_height, 1 );
  557.   XDrawImageString( theDisplay, theWindow, gc2, about_width_x+icon_width, about_height-(icon_height/2),
  558.         status_line, strlen( status_line ) );           
  559.  
  560.   /* Draw the calibration information in inches and centimeters. */
  561.  
  562.   XDrawLine( theDisplay, theWindow, gc3, about_width-3, about_height, about_width-3, about_height-pixels_per_inch_y );
  563.   XDrawLine( theDisplay, theWindow, gc3, about_width-3, about_height-pixels_per_cm_y,
  564.         about_width-3-8, about_height-pixels_per_cm_y );
  565.   XDrawLine( theDisplay, theWindow, gc3, about_width-3, about_height-2*pixels_per_cm_y,
  566.         about_width-3-8, about_height-2*pixels_per_cm_y );
  567.  
  568.   XDrawLine( theDisplay, theWindow, gc3, about_width-3, about_height, about_width-3-pixels_per_inch_x, about_height );
  569.   XDrawLine( theDisplay, theWindow, gc3, about_width-3-pixels_per_cm_x, about_height,
  570.         about_width-3-pixels_per_cm_x, about_height-8 );
  571.   XDrawLine( theDisplay, theWindow, gc3, about_width-3-2*pixels_per_cm_x, about_height,
  572.         about_width-3-2*pixels_per_cm_x, about_height-8 );
  573.   XDrawImageString( theDisplay, theWindow, gc3, about_width-calibration_width-20, about_height-pixels_per_cm_y, calibration,
  574.         strlen( calibration ) );
  575.  
  576. } /* end draw_misc_info */
  577.  
  578.  
  579.  
  580.  
  581. void
  582. draw_odometers()
  583. {
  584.   
  585.   if ( odometer_count == CURSOR || odometer_count == BOTH ) {
  586.     XDrawImageString( theDisplay, theWindow, gc, label_cursor_x, label_cursor_y, CURSOR_S, strlen( CURSOR_S ) );
  587.   } else {
  588.     XDrawImageString( theDisplay, theWindow, gc, label_cursor_x, label_cursor_y,  POINTER_S, strlen( POINTER_S ) );
  589.   }
  590.   XDrawRectangles( theDisplay, theWindow, gc, rectangles, 5 );
  591.   XFillRectangles( theDisplay, theWindow, gc, rectangles+5, 5 );
  592.   
  593.   XDrawRectangles( theDisplay, theWindow, gc, rectangles2, 5 );
  594.   XFillRectangles( theDisplay, theWindow, gc, rectangles2+5, 5 );
  595.   
  596.   if ( odometer_count == BOTH ) {
  597.     XDrawImageString( theDisplay, theWindow, gc, label_pointer_x, label_pointer_y, POINTER_S, strlen( POINTER_S ) );
  598.     
  599.     XDrawRectangles( theDisplay, theWindow, gc, rectangles3, 5 );
  600.     XFillRectangles( theDisplay, theWindow, gc, rectangles3+5, 5 );
  601.     
  602.     XDrawRectangles( theDisplay, theWindow, gc, rectangles4, 5 );
  603.     XFillRectangles( theDisplay, theWindow, gc, rectangles4+5, 5 );
  604.   }
  605.  
  606.   do_distances( True );        /* repaint odometer digits */
  607.   
  608. } /* end draw_odometers */
  609.  
  610.  
  611.  
  612.  
  613. void
  614. draw_odometer_digit( gc, rectangle, line, old_line )
  615.      GC                         gc;
  616.      XRectangle            rectangle;
  617.      char                       line[];
  618.      char                       old_line[];
  619. {
  620.  
  621.   if ( line[0] == old_line[0] )
  622.     return;
  623.  
  624.   XDrawImageString( theDisplay, theWindow, gc, rectangle.x+(font_info->max_bounds.lbearing),
  625.         rectangle.y+(font_info->max_bounds.ascent)+1, line+0, 1 );
  626.  
  627.   old_line[0] = line[0];
  628.  
  629. } /* end draw_odometer_digit */
  630.  
  631.  
  632.  
  633.  
  634. void
  635. finish_xodo()            /* finish xodo */
  636. {
  637.  
  638.   save_xodo();            /* update state information */
  639.  
  640.   XUnloadFont( theDisplay, font_info->fid );
  641.   XUnloadFont( theDisplay, font_info2->fid );
  642.   XFreeGC( theDisplay, gc);
  643.   XFreeGC( theDisplay, gc2);
  644.   XFreeGC( theDisplay, gc3);
  645.   XFreeGC( theDisplay, gc_reverse);
  646.   XFreeGC( theDisplay, gc2_reverse);
  647.   XCloseDisplay( theDisplay );
  648.  
  649. #if DEBUG
  650.   fclose( d );
  651. #endif
  652.  
  653.   exit( 0 );
  654.  
  655. } /* end finish_xodo */
  656.  
  657.  
  658.  
  659.  
  660. void
  661. highlight_action_window( gc, window, border )    /* either normal or highlighted */
  662.      GC                gc;
  663.      Window                     window;
  664.      unsigned long              border;
  665. {
  666.  
  667.   int                           i;
  668.  
  669.   if ( menu_active )
  670.     return;
  671.  
  672.   for ( i = 0; i < windata_count; i++ ) {
  673.     if ( window == windata[i].window ) {
  674.       XSetWindowBorder( theDisplay, windata[i].window, border );
  675.       XDrawImageString( theDisplay, windata[i].window, gc, font_info2->max_bounds.lbearing-3, 
  676.           font_info2->max_bounds.ascent+1, windata[i].text, strlen(windata[i].text) );
  677.       break;
  678.     } /* ifend */
  679.   } /* forend */
  680.  
  681.   if ( i == UNITS )
  682.     XCopyPlane( theDisplay, pulldown, windata[UNITS].window, gc, 0, 0, pulldown_width, pulldown_height, 1, 1, 1 );
  683.  
  684. } /* end highlight_action_window */
  685.  
  686.  
  687.  
  688.  
  689. void
  690. initialize_graphics_contexts()    /* initialize graphics context */
  691. {
  692.  
  693.   unsigned int                  line_width = 1;
  694.   int                           line_style = LineSolid;
  695.   int                           cap_style = CapRound;
  696.   int                           join_style = JoinMiter;
  697.   
  698.   fontname = pvt[P_FONTNAME].value.string_value;
  699.  
  700.   if ( (font_info = XLoadQueryFont( theDisplay, fontname )) == NULL ) {
  701.     fprintf( stderr, "%s:  cannot open %s font.\n", ProgramName, fontname );
  702.     exit( 1 );
  703.   }
  704.  
  705.   valuemask = GCFunction | GCForeground | GCBackground;
  706.   values.function = GXcopy;
  707.   values.foreground = theForegroundPixel;
  708.   values.background = theBackgroundPixel;
  709.   gc = XCreateGC( theDisplay, theWindow, valuemask, &values );
  710.   XSetFont( theDisplay, gc, font_info->fid );
  711.   XSetLineAttributes( theDisplay, gc, line_width, line_style, cap_style, join_style );
  712.  
  713.   valuemask = GCFunction | GCForeground | GCBackground;
  714.   values.function = GXcopy;
  715.   values.foreground = theBackgroundPixel;
  716.   values.background = theForegroundPixel;
  717.   gc_reverse = XCreateGC( theDisplay, theWindow, valuemask, &values );
  718.   XSetFont( theDisplay, gc_reverse, font_info->fid );
  719.   XSetLineAttributes( theDisplay, gc_reverse, line_width, line_style, cap_style, join_style );
  720.  
  721.   fontname = pvt[P_FONTNAME2].value.string_value;
  722.   if ( (font_info2 = XLoadQueryFont( theDisplay, fontname )) == NULL ) {
  723.     fprintf( stderr, "%s:  cannot open %s font.\n", ProgramName, fontname );
  724.     exit( 1 );
  725.   }
  726.  
  727.   valuemask = GCFunction | GCForeground | GCBackground;
  728.   values.function = GXcopy;
  729.   values.foreground = theForegroundPixel;
  730.   values.background = theBackgroundPixel;
  731.   gc2 = XCreateGC( theDisplay, theWindow, valuemask, &values );
  732.   XSetFont( theDisplay, gc2, font_info2->fid );
  733.   XSetLineAttributes( theDisplay, gc2, line_width, line_style, cap_style, join_style );
  734.  
  735.   valuemask = GCFunction | GCForeground | GCBackground;
  736.   values.function = GXcopy;
  737.   values.foreground = theBackgroundPixel;
  738.   values.background = theForegroundPixel;
  739.   gc2_reverse = XCreateGC( theDisplay, theWindow, valuemask, &values );
  740.   XSetFont( theDisplay, gc2_reverse, font_info2->fid );
  741.   XSetLineAttributes( theDisplay, gc2_reverse, line_width, line_style, cap_style, join_style );
  742.  
  743.   valuemask = GCFunction | GCForeground | GCBackground;
  744.   values.function = GXcopy;
  745.   values.foreground = theBorderPixel;
  746.   values.background = theBackgroundPixel;
  747.   gc3 = XCreateGC( theDisplay, theWindow, valuemask, &values );
  748.   XSetFont( theDisplay, gc3, font_info2->fid );
  749.   XSetLineAttributes( theDisplay, gc3, line_width, line_style, cap_style, join_style );
  750.  
  751. } /* end initialize_graphics_contexts */
  752.  
  753.  
  754.  
  755.  
  756. void
  757. initialize_menu()        /* initialize the Units menu */
  758. {
  759.  
  760.   char                          *string;
  761.   int                           i, x, y;
  762.   Pixmap                        theCursorSource, theCursorMask;
  763.  
  764.   string = distances[0].name;    /* find longest string */
  765.   for ( i = 1; i < menu_pane_count; i++ ) {
  766.     if( strlen( distances[i].name ) > strlen( string ) )
  767.       string = distances[i].name;
  768.   }
  769.   XTextExtents( font_info, string, strlen( string ), &direction, &ascent, &descent, &overall );
  770.   menu_width = overall.width + 4;
  771.   menu_pane_height = overall.ascent + overall.descent + 4;
  772.   menu_height = menu_pane_height * menu_pane_count;
  773.   x = window_width - menu_width - ( 2 * menu_border_width);
  774.   y = 0;
  775.  
  776.   theMenu = XCreateSimpleWindow( theDisplay, theWindow, x, y, menu_width, menu_height, menu_border_width, theBorderPixel,
  777.         theBackgroundPixel );
  778.  
  779.   for( i = 0; i < menu_pane_count; i++ ) {
  780.     distances[i].menu_pane = XCreateSimpleWindow( theDisplay, theMenu, 0, menu_height/menu_pane_count*i, menu_width,
  781.           menu_pane_height, menu_border_width = 1, theForegroundPixel, theBackgroundPixel );
  782.     XSelectInput( theDisplay, distances[i].menu_pane, EVENT_MASK3 );
  783.   }
  784.  
  785.   theCursorSource = XCreateBitmapFromData( theDisplay, theWindow, cursor_bits, cursor_width, cursor_height );
  786.   theCursorMask = XCreateBitmapFromData( theDisplay, theWindow, cursor_mask_bits, cursor_mask_width, cursor_mask_height );
  787.   theCursor = XCreatePixmapCursor( theDisplay, theCursorSource, theCursorMask, &theForegroundColor, &theBackgroundColor,
  788.         cursor_x_hot, cursor_y_hot );
  789.   XDefineCursor( theDisplay, theMenu, theCursor );
  790.  
  791.   XMapSubwindows( theDisplay, theMenu );
  792.  
  793. } /* end initialize_menu */
  794.  
  795.  
  796.  
  797.  
  798. void
  799. initialize_xodo()        /* initialize xodometer */
  800. {
  801.     int                GeometryStatus;
  802.     XSetWindowAttributes    theWindowAttributes;
  803.     XSizeHints            theSizeHints;
  804.     unsigned long        theWindowMask;
  805.     XWMHints            theWMHints;
  806.     Colormap            theColormap;
  807.     int                         i;
  808.     Window                    QueryRoot, QueryChild;
  809.     int                         AbsoluteX, AbsoluteY;
  810.     int                         RelativeX, RelativeY;
  811.     unsigned int                ModKeyMask;
  812.     XVisualInfo                 visual_info;
  813.     unsigned long               background;
  814.     char                        units[80];
  815.     char                        *X_default;
  816.     static char                 *argv[1] = {"xodo"};
  817.     int                         argc = 1;
  818.     long                        max_request_size;
  819.     struct sigaction            action;
  820.  
  821. #if DEBUG
  822.     d = fopen( "debug.log", "w" );
  823. #endif
  824.  
  825.     sigemptyset( &action.sa_mask ); /* disable all signals */
  826.  
  827.     action.sa_flags = 0;
  828.     action.sa_handler = finish_xodo;
  829.     if ( sigaction( SIGINT, &action, NULL ) != 0 ) {
  830.       fprintf( stderr, "Cannot set signal SIGINT!\n" );
  831.       exit( 1 );
  832.     }
  833.     action.sa_flags = 0;
  834.     action.sa_handler = save_xodo;
  835.     if ( sigaction( SIGHUP, &action, NULL ) != 0 ) {
  836.       fprintf( stderr, "Cannot set signal SIGHUP!\n" );
  837.       exit( 1 );
  838.     }
  839.  
  840.     ProgramName = pvt[P_HELP].unconverted_value;
  841.  
  842.     if ( strlen( pvt[P_DISPLAY].value.string_value ) < 1 ) {
  843.     pvt[P_DISPLAY].value.string_value = NULL;
  844.     }
  845.  
  846.     if ( ( theDisplay = XOpenDisplay( pvt[P_DISPLAY].value.string_value ) ) == NULL ) {
  847.     fprintf( stderr, "%s: Can't open display", ProgramName );
  848.     if ( pvt[P_DISPLAY].value.string_value != NULL ) {
  849.         fprintf( stderr, " %s.\n", pvt[P_DISPLAY].value.string_value );
  850.     } else {
  851.         fprintf( stderr, ".\n" );
  852.     }
  853.     exit( 1 );
  854.     }
  855.  
  856.     if ( strlen( pvt[P_GEOMETRY].value.string_value ) < 1 ) {
  857.     pvt[P_GEOMETRY].value.string_value = NULL;
  858.     }
  859.  
  860.     /* For all unspecified evaluate_parameters command line parameters see if there is an X default value. */
  861.  
  862.     for ( i = 0 ; i < P_NUMBER_OF_PARAMETERS; i++ ) { /* for all evaluate_parameters parameters */
  863.       if ( ! pvt[i].specified ) {
  864.     X_default = XGetDefault( theDisplay, ProgramName, pvt[i].parameter );
  865.     if ( X_default != NULL ) {
  866.       pvt[i].unconverted_value = X_default;
  867.       evap_type_conversion( &pvt[i] ); /* convert string to proper type */
  868.     } /* ifend non-null X default for this parameter */
  869.       } /* ifend unspecified parameter */
  870.     } /* forend all evaluate_parameters parameters */
  871.  
  872.     max_request_size = XMaxRequestSize( theDisplay );
  873.     if ( ((max_request_size - 3) / 3) < 10 ) {
  874.       fprintf( stderr, "XMaxRequestSize is too small for xodo the run!\n");
  875.       exit( 1 );
  876.     }
  877.  
  878.     motion_buffer_size = XDisplayMotionBufferSize( theDisplay );
  879.  
  880.     if ( strcmp( pvt[P_ODOMETER].value.key_value, "cursor" ) == 0 )
  881.       odometer_count = CURSOR;
  882.     else if ( strcmp( pvt[P_ODOMETER].value.key_value, "pointer" ) == 0 )
  883.       odometer_count = POINTER;
  884.  
  885.     autosave_ticks = pvt[P_ODOMETER_AUTOSAVE_TIME].value.integer_value * 60 * 1000000 /
  886.           pvt[P_MICROSECOND_INTERVAL_TIME].value.integer_value;
  887.     autosave_count = autosave_ticks;
  888.  
  889.     display_widthmm = pvt[P_DISPLAY_WIDTH_MILLIMETERS].value.integer_value;
  890.     display_heightmm = pvt[P_DISPLAY_HEIGHT_MILLIMETERS].value.integer_value;
  891.     display_width = pvt[P_DISPLAY_WIDTH_PIXELS].value.integer_value;
  892.     display_height = pvt[P_DISPLAY_HEIGHT_PIXELS].value.integer_value;
  893.     X_mm_per_pixel = (double)display_widthmm / display_width;
  894.     Y_mm_per_pixel = (double)display_heightmm / display_height;
  895.     pixels_per_inch_x = (int)( 25.4 / X_mm_per_pixel );
  896.     pixels_per_inch_y = (int)( 25.4 / Y_mm_per_pixel );
  897.     pixels_per_cm_x = (int)( 10.0 / X_mm_per_pixel );
  898.     pixels_per_cm_y = (int)( 10.0 / Y_mm_per_pixel );
  899.     aspect = (float)(X_mm_per_pixel / Y_mm_per_pixel);
  900.  
  901.     total_cursor_distance = 0.0;
  902.     trip_cursor_distance = 0.0;
  903.     total_pointer_distance = 0.0;
  904.     trip_pointer_distance = 0.0;
  905.  
  906.     for( i=0; i < menu_pane_count; i++ ) { /* default units = kilometers */
  907.       if( strcmp( distances[i].name, "kilometers" ) == 0 )
  908.     distances_ordinal = i;
  909.     }
  910.  
  911.     OF = fopen( pvt[P_ODOMETER_FILE].value.file_value, "r" );
  912.     if ( OF == NULL && errno != ENOENT ) {
  913.       perror("Cannot open odometer_file");
  914.       exit( 1 );
  915.     } else if ( OF != NULL ) {
  916.       fscanf( OF, "%lf %lf %s", &total_cursor_distance, &total_pointer_distance, units );
  917.       for( i=0; i < menu_pane_count; i++ ) {
  918.     if( strcmp( distances[i].name, units ) == 0 )
  919.       distances_ordinal = i;
  920.       }
  921.       fclose( OF );
  922.     }
  923.  
  924.     GeometryStatus = XParseGeometry( pvt[P_GEOMETRY].value.string_value, &WindowPointX, &WindowPointY, &WindowWidth,
  925.           &WindowHeight );
  926.  
  927.     if ( !( GeometryStatus & XValue ) ) {
  928.       WindowPointX = 1;
  929.     } else if ( GeometryStatus & XNegative ) {
  930.       WindowPointX = display_width + WindowPointX;
  931.     }
  932.     if ( !( GeometryStatus & YValue ) ) {
  933.       WindowPointY = 1;
  934.     } else if ( GeometryStatus & YNegative ) {
  935.       WindowPointY = display_height + WindowPointY;
  936.     }
  937.     if ( !( GeometryStatus & WidthValue ) ) {
  938.       WindowWidth = window_width;
  939.     }
  940.     if ( !( GeometryStatus & HeightValue ) ) {
  941.       WindowHeight = window_height;
  942.     }
  943.  
  944.     theScreen = DefaultScreen( theDisplay );
  945.     theDepth = DefaultDepth( theDisplay, theScreen );
  946.     theColormap = DefaultColormap( theDisplay, theScreen );
  947.     theForegroundPixel = BlackPixel( theDisplay, theScreen );
  948.     theBackgroundPixel = WhitePixel( theDisplay, theScreen );
  949.     theBorderPixel = theForegroundPixel;
  950.  
  951.     if ( theDepth > 1 ) {    /* if possible color monitor */
  952.  
  953.       i = DirectColor;        /* StaticGray, GrayScale, StaticColor, PseudoColor, TrueColor, DirectColor */
  954.       while ( ! XMatchVisualInfo( theDisplay, theScreen, theDepth, i--, &visual_info ) )
  955.     ; /* whilend */
  956.  
  957.       if ( i >= StaticColor ) {
  958.     if ( ! XAllocNamedColor( theDisplay, theColormap, pvt[P_BACKGROUND].value.name_value, &theBackgroundColor,
  959.               &theExactColor ) ) {
  960.       fprintf( stderr, "%s: Can't XAllocNamedColor( \"%s\" ).\n", ProgramName, pvt[P_BACKGROUND].value.name_value );
  961.       exit( 1 );
  962.     }
  963.     if ( ! XAllocNamedColor( theDisplay, theColormap, pvt[P_FOREGROUND].value.name_value, &theForegroundColor,
  964.               &theExactColor ) ) {
  965.       fprintf( stderr, "%s: Can't XAllocNamedColor( \"%s\" ).\n", ProgramName, pvt[P_FOREGROUND].value.name_value );
  966.       exit( 1 );
  967.     }
  968.     if ( ! XAllocNamedColor( theDisplay, theColormap, pvt[P_BORDER].value.name_value, &theBorderColor, &theExactColor ) ) {
  969.       fprintf( stderr, "%s: Can't XAllocNamedColor( \"%s\" ).\n", ProgramName, pvt[P_BORDER].value.name_value );
  970.       exit( 1 );
  971.     }
  972.  
  973.     theForegroundPixel = theForegroundColor.pixel;
  974.     theBackgroundPixel = theBackgroundColor.pixel;
  975.     theBorderPixel = theBorderColor.pixel;
  976.       } /* ifend color visual */
  977.  
  978.     } /* ifend depth > 1 */
  979.  
  980.     theWindowAttributes.border_pixel = theBorderPixel;
  981.     theWindowAttributes.background_pixel = theBackgroundPixel;
  982.     theWindowAttributes.override_redirect = False;
  983.     theWindowAttributes.event_mask = EVENT_MASK1;
  984.  
  985.     theWindowMask = CWBackPixel | CWBorderPixel    | CWEventMask | CWOverrideRedirect;
  986.  
  987.     theWindow = XCreateWindow( theDisplay, RootWindow( theDisplay, theScreen ), WindowPointX, WindowPointY, WindowWidth,
  988.           WindowHeight, BorderWidth, theDepth, InputOutput, CopyFromParent, theWindowMask, &theWindowAttributes );
  989.  
  990.     initialize_graphics_contexts(); /* initialize graphics contexts */
  991.  
  992.     theIconPixmap = XCreateBitmapFromData( theDisplay, theWindow, icon_bits, icon_width, icon_height );
  993.     pulldown = XCreateBitmapFromData( theDisplay, theWindow, pulldown_bits, pulldown_width, pulldown_height );
  994.  
  995.     theWMHints.icon_pixmap = theIconPixmap;
  996.     if ( pvt[P_ICONIC].specified ) {
  997.     theWMHints.initial_state = IconicState;
  998.     } else {
  999.     theWMHints.initial_state = NormalState;
  1000.     }
  1001.     theWMHints.flags = IconPixmapHint | StateHint;
  1002.  
  1003.     XSetWMHints( theDisplay, theWindow, &theWMHints );
  1004.  
  1005.     theSizeHints.flags = PPosition | PSize; /* PMinSize perhaps */
  1006.     theSizeHints.x = WindowPointX;
  1007.     theSizeHints.y = WindowPointY;
  1008.     theSizeHints.width = WindowWidth;
  1009.     theSizeHints.height = WindowHeight;
  1010.     theSizeHints.min_width = window_width;
  1011.     theSizeHints.min_height = window_height;
  1012.  
  1013.     XSetNormalHints( theDisplay, theWindow, &theSizeHints );
  1014.  
  1015.     if ( strlen( pvt[P_TITLE].value.string_value ) >= 1 ) {
  1016.     XStoreName( theDisplay, theWindow, pvt[P_TITLE].value.string_value );
  1017.     XSetIconName( theDisplay, theWindow, pvt[P_TITLE].value.string_value );
  1018.     } else {
  1019.     XStoreName( theDisplay, theWindow, ProgramName );
  1020.     XSetIconName( theDisplay, theWindow, ProgramName );
  1021.     }
  1022.  
  1023.     XSetCommand( theDisplay, theWindow, argv, argc );
  1024.     atom_wm_save_yourself = XInternAtom( theDisplay, "WM_SAVE_YOURSELF", False );
  1025.     status = XSetWMProtocols( theDisplay, theWindow, &atom_wm_save_yourself, 1 );
  1026.  
  1027.     for( i = 0; i < windata_count; i++ ) { /* make Action buttons */
  1028.       if ( odometer_count != BOTH && i == TRIP2 )
  1029.     continue;        /* skip second trip odometer if only 1 odometer */
  1030.       background = theBackgroundPixel;
  1031.       if ( i == TRIP1 || i == TRIP2 )
  1032.     background = theBorderPixel;
  1033.       windata[i].window = XCreateSimpleWindow( theDisplay, theWindow, windata[i].x, windata[i].y, windata[i].width,
  1034.             windata[i].height, windata[i].border, theBorderPixel, background );
  1035.       XSelectInput( theDisplay, windata[i].window, EVENT_MASK2 );
  1036.     } /* forend */
  1037.  
  1038.     XQueryPointer( theDisplay, theWindow, &QueryRoot, &QueryChild, &AbsoluteX, &AbsoluteY, &RelativeX, &RelativeY, &ModKeyMask );
  1039.     PointerX = AbsoluteX;
  1040.     PointerY = AbsoluteY;
  1041.  
  1042.     XGetPointerControl( theDisplay, &accel_numerator, &accel_denominator, &threshold);
  1043.     acceleration = (double)accel_numerator / (double)accel_denominator;
  1044.  
  1045.     sprintf( status_line, "S=%.1f T=%d A=%.1f", pvt[P_POINTER_SCALE_FACTOR].value.real_value, threshold, acceleration );
  1046.     strncpy( distances_human+2, distances[distances_ordinal].abbreviation, 3 );
  1047.  
  1048.     XMapSubwindows( theDisplay, theWindow );
  1049.  
  1050.     compute_font_and_resize_data( font_info );
  1051.  
  1052.     XGetGeometry( theDisplay, theWindow, &theRoot, &WindowPointX, &WindowPointY, &WindowWidth, &WindowHeight, &BorderWidth,
  1053.           &theDepth );
  1054.  
  1055.     XTextExtents( font_info, about[2], strlen( about[2] ), &direction, &ascent, &descent, &overall );
  1056.     about_width = overall.width + 4 + about_width_x;
  1057.     about_height = ( overall.ascent + overall.descent + 4 ) * about_count;
  1058.     about_height += icon_height + font_height + 1;
  1059.     i = strlen( about[0] );    /* update version information */
  1060.     strncpy( about[0]+i-3, VERSION, 3 );
  1061.  
  1062.     XTextExtents( font_info2, calibration, strlen( calibration ), &direction, &ascent, &descent, &overall );
  1063.     calibration_width = overall.width + 4;
  1064.  
  1065.     initialize_menu();        /* initialize the Units menu windows */
  1066.  
  1067.     XMapWindow( theDisplay, theWindow );
  1068.     XFlush( theDisplay );
  1069.  
  1070. } /* end initialize_xodo */
  1071.  
  1072.  
  1073.  
  1074.  
  1075. Bool
  1076. process_event()            /* handle all X events */
  1077. {
  1078.   XEvent                        theEvent;
  1079.   int                           new_width, new_height;
  1080.   int                           i;
  1081.  
  1082.   /* Check for ClientMessage type WM_SAVE_YOURSELF to save state information. */
  1083.  
  1084.   if ( XCheckTypedEvent( theDisplay, ClientMessage, &theEvent ) == True ) {
  1085.     if ( theEvent.xclient.data.l[0] == atom_wm_save_yourself )
  1086.       finish_xodo();        /* save state information and exit */
  1087.   } /* ifend ClientMessage received */
  1088.   
  1089.   while ( XCheckMaskEvent( theDisplay, ALL_EVENTS_MASK, &theEvent ) ) {
  1090.  
  1091.     switch ( theEvent.type ) {
  1092.       
  1093.     case MapNotify:      case MappingNotify:   case GraphicsExpose:   case NoExpose:
  1094.     case SelectionClear: case SelectionNotify: case SelectionRequest:
  1095.       
  1096.       break;            /* misc */
  1097.       
  1098.     case ClientMessage:
  1099.  
  1100.       finish_xodo();        /* will never get here via XCheckMaskEvent! */
  1101.       
  1102.       break;            /* ClientMessage */
  1103.       
  1104.     case ConfigureNotify:
  1105.       if ( theEvent.xconfigure.window == theWindow ) {
  1106.     WindowWidth = theEvent.xconfigure.width;
  1107.     WindowHeight = theEvent.xconfigure.height;
  1108.     if ( theEvent.xconfigure.x != 0 )
  1109.       WindowPointX = theEvent.xconfigure.x;
  1110.     if ( theEvent.xconfigure.y != 0 )
  1111.       WindowPointY = theEvent.xconfigure.y;
  1112.     BorderWidth = theEvent.xconfigure.border_width;
  1113.       }
  1114.       
  1115.       break;            /* ConfigureNotify */
  1116.     
  1117.     case Expose:
  1118.       
  1119.       if ( theEvent.xexpose.count == 0 ) {
  1120.     
  1121.     if ( theEvent.xexpose.window == theWindow ) {
  1122.       draw_odometers();
  1123.       draw_misc_info();
  1124.       draw_action_windows( gc2 );
  1125.         }
  1126.     
  1127.     draw_menu_panes( gc, theEvent.xexpose.window );
  1128.  
  1129.       } /* ifend event count = 0 */
  1130.       
  1131.       break;            /* Expose */
  1132.       
  1133.     case EnterNotify:
  1134.  
  1135.       if ( button_depressed ) {
  1136.     if (theEvent.xcrossing.window == windata[UNITS].window)
  1137.       draw_menu();
  1138.     else
  1139.       highlight_action_window( gc2_reverse, theEvent.xcrossing.window, theForegroundPixel );
  1140.       }
  1141.  
  1142.       draw_menu_panes( gc_reverse, theEvent.xcrossing.window );
  1143.     
  1144.       break;            /* EnterNotify */
  1145.       
  1146.     case LeaveNotify:
  1147.  
  1148.       highlight_action_window( gc2, theEvent.xcrossing.window, theBorderPixel );
  1149.       
  1150.       draw_menu_panes( gc, theEvent.xcrossing.window );
  1151.       
  1152.       break;            /* LeaveNotify */
  1153.       
  1154.     case ButtonPress:
  1155.  
  1156.       button_depressed = True;
  1157.   
  1158.       if ( theEvent.xbutton.window == windata[UNITS].window ) {
  1159.     draw_menu();
  1160.     break;
  1161.       }      
  1162.  
  1163.       highlight_action_window( gc2_reverse, theEvent.xbutton.window, theForegroundPixel );
  1164.  
  1165.       break;            /* ButtonPress */
  1166.  
  1167.     case ButtonRelease:
  1168.  
  1169.       button_depressed = False;
  1170.  
  1171.       highlight_action_window( gc2, theEvent.xbutton.window, theBorderPixel );
  1172.  
  1173.       if ( menu_active == True ) {
  1174.     XUngrabPointer( theDisplay, CurrentTime );
  1175.     XUnmapWindow( theDisplay, theMenu );
  1176.     XMoveResizeWindow( theDisplay, theWindow, menu_x_old, menu_y_old, menu_width_old, menu_height_old );
  1177.     menu_active = False;
  1178.     for ( i = 0; i < menu_pane_count; i++ ) {
  1179.       if ( theEvent.xbutton.window == distances[i].menu_pane ) {
  1180.         distances_ordinal = i;
  1181.         strncpy( distances_human+2, distances[distances_ordinal].abbreviation, 3 );
  1182.         draw_misc_info();
  1183.       } /* ifend button release occurred in a menu pane */
  1184.     } /* forend */
  1185.     break; /* case ButtonRelease */
  1186.       } /* ifend menu_active */
  1187.  
  1188.       if ( theEvent.xbutton.window == windata[TRIP1].window ) {
  1189.     if ( odometer_count == CURSOR || odometer_count == BOTH ) {
  1190.       trip_cursor_distance = 0.0;
  1191.     } else {
  1192.       trip_pointer_distance = 0.0;
  1193.     }
  1194.       } else if ( theEvent.xbutton.window == windata[TRIP2].window ) {
  1195.     trip_pointer_distance = 0.0;
  1196.       } else if ( theEvent.xbutton.window == windata[QUIT].window ) {
  1197.     finish_xodo();        /* update distance totals */
  1198.       } else if ( theEvent.xbutton.window == windata[UNITS].window ) {
  1199.     XUnmapWindow( theDisplay, theMenu );
  1200.       } else if ( theEvent.xbutton.window == windata[ABOUT].window ) {
  1201.     if ( about_active ) {
  1202.       about_active = False;
  1203.       windata[ABOUT].text = "About";
  1204.       XMoveResizeWindow( theDisplay, theWindow, about_x_old, about_y_old, about_width_old, about_height_old );
  1205.     } else {
  1206.       about_active = True;
  1207.       about_width_old = WindowWidth;
  1208.       about_height_old = WindowHeight;
  1209.       windata[ABOUT].text = " OK! ";
  1210.       if ( WindowWidth < about_width ) 
  1211.         new_width = about_width;
  1212.       else
  1213.         new_width = WindowWidth;
  1214.       if ( WindowHeight < about_height ) 
  1215.         new_height = about_height;
  1216.       else
  1217.         new_height = WindowHeight;
  1218.       about_x_old = WindowPointX;
  1219.       about_y_old = WindowPointY;
  1220.       if ( WindowPointX + new_width > display_width )
  1221.         about_x_old = display_width - new_width - 10;
  1222.       if ( WindowPointY + new_height > display_height )
  1223.         about_y_old = display_height - new_height - 10;
  1224.       XMoveResizeWindow( theDisplay, theWindow, about_x_old, about_y_old, new_width, new_height+5 );
  1225.       about_x_old = WindowPointX;
  1226.       about_y_old = WindowPointY;
  1227.     }
  1228.       } else if ( theEvent.xbutton.button == Button2 ) {
  1229.     trip_cursor_distance = 0.0;
  1230.     trip_pointer_distance = 0.0;
  1231.       } /* ifend */
  1232.  
  1233.       break;            /* ButtonRelease */
  1234.     
  1235.     default:
  1236.  
  1237.       break;            /* unknown event */
  1238.  
  1239.     } /* casend event type */
  1240.  
  1241.   } /* whilend */
  1242.  
  1243.   return( True );
  1244.  
  1245. } /* end process_event */
  1246.  
  1247.  
  1248.  
  1249.  
  1250. void
  1251. process_pointer()        /* do stuff every time period */
  1252. {
  1253.   struct timeval                timer;
  1254.   
  1255.   timer.tv_sec = 0;
  1256.   timer.tv_usec = pvt[P_MICROSECOND_INTERVAL_TIME].value.integer_value;
  1257.   
  1258.   do {
  1259.     
  1260.     do_distances( False );
  1261.     
  1262.     if ( --autosave_count <= 0 ) {
  1263.       autosave_count = autosave_ticks;
  1264.       save_xodo();        /* update state information */
  1265.     }
  1266.     
  1267.     if ( pvt[P_MICROSECOND_INTERVAL_TIME].value.integer_value <= 999999 )
  1268.       select( 0, NULL, NULL, NULL, &timer );
  1269.     else
  1270.       sleep ( (unsigned)pvt[P_MICROSECOND_INTERVAL_TIME].value.integer_value / 1000000 );
  1271.     
  1272.   } while ( process_event() );
  1273.  
  1274. } /* end process_pointer */
  1275.  
  1276.  
  1277.  
  1278.  
  1279. void
  1280. save_xodo()            /* update xodo distances */
  1281. {
  1282.  
  1283.   OF = fopen( pvt[P_ODOMETER_FILE].value.file_value, "w" );
  1284.   if ( OF == NULL ) {
  1285.     perror("Cannot open odometer_file for write!");
  1286.   } else {
  1287.     fprintf( OF, "%f %f %s\n", total_cursor_distance, total_pointer_distance, distances[distances_ordinal].name );
  1288.     fclose( OF );
  1289.   }
  1290.  
  1291. } /* end save_xodo */
  1292.  
  1293.  
  1294.  
  1295. int
  1296. main( argc, argv )
  1297.     int        argc;
  1298.     char    *argv[];
  1299. {
  1300.   
  1301.   evap( &argc, &argv, pdt, NULL, pvt ); /* evaluate parameters */
  1302.   
  1303.   initialize_xodo();        /* all other xodometer initialization */
  1304.  
  1305.   process_pointer();        /* watch the pointing device */
  1306.   
  1307.   finish_xodo();        /* update distances for next time */
  1308.  
  1309.   exit( 0 );            /* success, just in case */
  1310.  
  1311. } /* end xodo main */
  1312.