Advertisement
custompc

Xobsdview Xosview

Mar 4th, 2024
986
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 30.83 KB | Source Code | 0 0
  1. /*
  2. Neat little Xosview like system monitor for OpenBSD
  3. See: https://www.volkerschatz.com/unix/homebrew.html
  4. Compiled nicely on OpenBSD 7.4 - make as line below
  5. cc -o xobsdview -I/usr/X11R6/include xobsdview.c -L/usr/X11R6/lib -lX11 -lm
  6. */
  7.  
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <stdint.h>
  11. #include <unistd.h>
  12. #include <string.h>
  13. #include <math.h>
  14. #include <errno.h>
  15. #include <sys/time.h>
  16. #include <sys/types.h>
  17. //#include <sys/ioctl.h>
  18. #include <sys/sysctl.h>
  19. #include <sys/select.h>     /* for struct timespec needed in sched.h */
  20. #include <sys/sched.h>      /* for CPUSTATES and CP_* state constants */
  21. #include <net/if.h>
  22. #include <net/route.h>
  23. #include <sys/disk.h>
  24. #include <sys/sensors.h>
  25.  
  26. #include <X11/Xlib.h>
  27. #include <X11/Xutil.h>
  28. #include <X11/Xos.h>
  29. #include <X11/keysym.h>
  30.  
  31.  
  32. #define WINWIDTH    150
  33. #define WINHEIGHT   300
  34. #define PADDING     2
  35. // #define FONTNAME "6x10"
  36. #define FONTNAME "-misc-fixed-*-*-*-*-10-*-*-*-*-*-*-*"
  37.  
  38. #define METERMINWIDTH  40
  39. #define METERMINHEIGHT 4
  40. #define METERSPLIT  1   /* display lowpass-filtered stats in lower half if != 0 */
  41. #define METERDECAY2 1.0 /* decay time to half value in seconds */
  42.  
  43. #define MAXLOAD     5
  44. #define MAXDISKBW   (300ull << 20)    /* disk bandwidth in bytes/s, in+out */
  45. #define MAXNETBW    (25ull << 20)   /* network bandwidth in bytes/s, in+out */
  46.  
  47. #define LABELCOL    0
  48. #define VALUECOL    0x808080
  49. #define IDLECOL     0x7FFFD4
  50. #define PSUCOL      0x20B2AA
  51.  
  52. #ifndef CPUSTATES
  53. #define CPUSTATES   6
  54. #endif
  55. #define MEMSTATES   5
  56.  
  57. /* confer SC_* constants in /usr/include/sys/sched.h */
  58. static const char *cpustabbr[CPUSTATES]= { "US", "NI", "SY", "SP", "IN", "ID" };
  59. static const uint32_t cpustcol[CPUSTATES]= { 0x2E8B57, 0xD0C000, 0xFFA500, 0xB060D3, 0xFF0000, IDLECOL };
  60. /* see https://en.wikipedia.org/wiki/X11_color_names for standard colours */
  61.  
  62. static const char *memabbr[MEMSTATES]= { "AC", "IN", "WR", "CA", "FR" };
  63. static const uint32_t memcol[MEMSTATES]= { 0xFF, 0x90B8D0, 0xB060D3, 0xFF0000, IDLECOL };
  64.  
  65. static const char *swapabbr[3]= { "ONLY", "USED", "FREE" };
  66. static const uint32_t swapcol[3]= { 0xFF, 0x90B8D0, IDLECOL };
  67.  
  68. static const char *ioabbr[3]= { "IN", "OUT", "IDLE" };
  69. static const char *diskabbr[3]= { "READ", "WRITE", "IDLE" };
  70. static const uint32_t iocol[3]= { 0x87CEEB, 0x6A5ACD, IDLECOL };
  71.  
  72. #define NWARNCOL 4
  73. static const uint32_t warncol[NWARNCOL]= { 0x2E8B57, 0xD0C000, 0xFFA500, 0xFF0000 };
  74. static const uint32_t chargingcol[2]= { 0x57EB90, IDLECOL };
  75.  
  76.  
  77. /* Update interval in seconds */
  78. #define INTERVAL    0.1
  79.  
  80.  
  81. typedef struct {
  82.     int firstcall, ncpus;
  83.     char *tabbuf;
  84.     size_t tabbufsize;
  85.     double loadavg[3];
  86.     double *cpus;
  87.     struct cpustats *cpustates, *oldcpustates;
  88.     double membytes[MEMSTATES], swapbytes[3];
  89.     double memtotal, swaptotal, pagein, pageout;
  90.     int oldpageins, oldpageouts;
  91.     uint64_t netin, netout, oldtotalnetin, oldtotalnetout;
  92.     uint64_t diskin, diskout, oldtotaldiskin, oldtotaldiskout;
  93.     int sensordev_psu, sensordev_bat, psu_on;
  94.     int64_t bat_curr, bat_max;
  95.     double *filtered;
  96. }
  97. obv_stats;
  98.  
  99. typedef struct {
  100.     Display *disp;
  101.     Window win;
  102.     GC gc;
  103.     XGCValues gcval;
  104.     char *name;
  105.     int windowx, windowy, havepos;
  106.     unsigned windoww, windowh;
  107.     int labelx, label0y, valuex, meterx, metery, meteroff;
  108.     unsigned meterw, meterh;        /* without frame */
  109.     unsigned fontasc, fontdesc, fontw, fontgap;
  110. }
  111. obv_view;
  112.  
  113. int init(obv_stats *st);
  114. void dealloc(obv_stats *st);
  115. void getstat(obv_stats *st);
  116. void update_filtered(obv_stats *st, int firstcall);
  117. void init_view(obv_view *view);
  118. void resize_view(obv_view *view, unsigned ncpus);
  119. void update_view(obv_view *view, obv_stats *st);
  120. void exit_view(obv_view *view);
  121. void draw_meter(obv_view *view, unsigned metind, int split, const double *fractions, const uint32_t *colours, unsigned n);
  122. void draw_warnmeter(obv_view *view, unsigned metind, int split, double value, int reverse);
  123. void draw_value(obv_view *view, unsigned metind, int type, double value);
  124. void draw_key(obv_view *view, unsigned metind, const char **strs, const uint32_t *colours, int n);
  125. void sprintbytes(char *dest, double bytes);
  126.  
  127.  
  128. int main(int argc, char **argv)
  129. {
  130.     XEvent event;
  131.     KeySym key;
  132.     Atom protocol[2], message;
  133.     XRectangle cliprect;
  134.     obv_stats st;
  135.     obv_view v= { .windoww= WINWIDTH, .windowh= WINHEIGHT, .windowx= 0, .windowy= 0, .name= "xobsdview", .havepos= 0 };
  136.     int argind, count, quit, updated;
  137.  
  138.     for( argind= 1; argind < argc; argind += 2 ) {
  139.         if( !strcmp(argv[argind], "-geometry") && argind < argc-1 ) {
  140.             count= sscanf(argv[argind+1], "%ux%u%d%d", &v.windoww, &v.windowh, &v.windowx, &v.windowy);
  141.             v.havepos= count == 4;
  142.         }
  143.         else if( !strcmp(argv[argind], "-name") && argind < argc-1 )
  144.             v.name= argv[argind+1];
  145.         else {
  146.             fprintf(stderr, "usage: xobsdview [ -geometry <geometry> ] [ -name <string> ]\n");
  147.             return 1;
  148.         }
  149.     }
  150.  
  151.     init(&st);
  152.     getstat(&st);
  153.     if( METERSPLIT )
  154.         update_filtered(&st, 1);
  155.     init_view(&v);
  156.  
  157.     XSelectInput(v.disp, v.win, KeyPressMask|KeyReleaseMask|ExposureMask|StructureNotifyMask );
  158.     protocol[0]= XInternAtom(v.disp, "WM_PROTOCOLS", False );
  159.     protocol[1]= XInternAtom(v.disp, "WM_DELETE_WINDOW", False );
  160.     XSetWMProtocols( v.disp, v.win, protocol, 2 );
  161.     quit= 0;
  162.     do {
  163.         updated= 0;
  164.         while( XPending(v.disp) > 0 ) {
  165.             XNextEvent( v.disp, &event );
  166.             if( event.type == KeyPress ) {
  167.                 key= XLookupKeysym( &event.xkey, 0 );
  168.                 if( key == XK_q )
  169.                     quit= 1;
  170.             }
  171.             else if( event.type == ClientMessage ) {
  172.                 if( event.xclient.message_type == protocol[0] ) {
  173.                     if( event.xclient.format == 8 )
  174.                         message= event.xclient.data.b[0];
  175.                     else if( event.xclient.format == 16 )
  176.                         message= event.xclient.data.s[0];
  177.                     else if( event.xclient.format == 32 )
  178.                         message= event.xclient.data.l[0];
  179.                     else message= protocol[1] - 1;
  180.                     if( message == protocol[1] )
  181.                         quit= 1;
  182.                 }
  183.             }
  184.             else if( event.type == ConfigureNotify ) {
  185.                 if( v.windoww != event.xconfigure.width || v.windowh != event.xconfigure.height ) {
  186.                     v.windoww= event.xconfigure.width;
  187.                     v.windowh= event.xconfigure.height;
  188.                     resize_view(&v, st.ncpus);
  189.                     update_view(&v, &st);
  190.                     updated= 1;
  191.                 }
  192.             }
  193.             else if( event.type == Expose && event.xexpose.count == 0 ) {
  194.                 resize_view(&v, st.ncpus);
  195.                 update_view(&v, &st);
  196.                 updated= 1;
  197.             }
  198.         }
  199.         if( ! updated )
  200.             update_view(&v, &st);
  201.         usleep(1e6 * INTERVAL);
  202.         getstat(&st);
  203.         if( METERSPLIT )
  204.             update_filtered(&st, 0);
  205.     }
  206.     while( ! quit );
  207.     exit_view(&v);
  208.     dealloc(&st);
  209.     return 0;
  210. }
  211.  
  212.  
  213. static int sensordev_mib[]= { CTL_HW, HW_SENSORS, /* sensor device index */ 0 };
  214.  
  215. int init(obv_stats *st)
  216. {
  217.     struct sensordev sd;
  218.     size_t size;
  219.     int status, ind, typeind;
  220.  
  221.     st->sensordev_psu= -1;
  222.     st->sensordev_bat= -1;
  223.     for( ind= 0; st->sensordev_psu < 0 || st->sensordev_bat < 0; ++ind ) {
  224.         sensordev_mib[2]= ind;
  225.         size= sizeof(sd);
  226.         status= sysctl(sensordev_mib, 3, &sd, &size, NULL, 0);
  227.         if( status < 0 || size < sizeof(sd) )
  228.             break;
  229.         if( !strcmp(sd.xname, "acpiac0") )
  230.             st->sensordev_psu= ind;
  231.         else if( !strcmp(sd.xname, "acpibat0") )
  232.             st->sensordev_bat= ind;
  233.     }
  234.     st->ncpus= sysconf(_SC_NPROCESSORS_CONF);
  235.     st->cpustates= malloc(2 * st->ncpus * sizeof(*st->cpustates));
  236.     st->oldcpustates= st->cpustates + st->ncpus;
  237.     st->cpus= malloc(CPUSTATES * st->ncpus * sizeof(double));
  238.     st->memtotal= 1;
  239.     st->swaptotal= 1;
  240.     st->tabbufsize= 2048;
  241.     st->tabbuf= malloc(st->tabbufsize);
  242.     if( METERSPLIT )
  243.         st->filtered= calloc(st->ncpus*CPUSTATES + MEMSTATES + 3 + 3 * 2, sizeof(double));
  244.     else
  245.         st->filtered= NULL;
  246.     st->firstcall= 1;
  247.     return st->cpustates && st->cpus && st->tabbuf && (!METERSPLIT || st->filtered);
  248. }
  249.  
  250. void dealloc(obv_stats *st)
  251. {
  252.     free(st->cpustates);
  253.     st->cpustates= NULL;
  254.     free(st->cpus);
  255.     st->cpus= NULL;
  256.     free(st->tabbuf);
  257.     st->tabbuf= NULL;
  258.     free(st->filtered);
  259.     st->filtered= NULL;
  260. }
  261.  
  262.  
  263. static int cpustats_mib[] = { CTL_KERN, KERN_CPUSTATS, /* CPU index */ 0 };
  264.     /* for struct cpustats, see /usr/include/sys/sched.h */
  265. static const int uvmexp_mib[]= { CTL_VM, VM_UVMEXP };
  266.     /* for struct uvmexp, see /usr/include/uvm/uvmexp.h */
  267. static const int iflist_mib[]= { CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, 0 };
  268.     /* for structs rt_msghdr and if_msghdr, see /usr/include/net/route.h and if.h */
  269. static const int diskstats_mib[]= { CTL_HW, HW_DISKSTATS };
  270.     /* for struct diskstats, see /usr/include/sys/disk.h */
  271. static int sensor_mib[]= { CTL_HW, HW_SENSORS, /* sensor device index */ 0, /* sensor type */ 0, /* sensor index */ 0 };
  272.  
  273. void getstat(obv_stats *st)
  274. {
  275.     struct uvmexp ue;
  276.     struct if_msghdr *ifm;
  277.     struct diskstats *ds;
  278.     struct sensor se;
  279.     char *tryalloc;
  280.     double diff, total;
  281.     size_t size, needsize, off;
  282.     int cpuind, stateind;
  283.  
  284.     getloadavg(st->loadavg, 3);
  285.     for( cpuind= 0; cpuind < st->ncpus; ++cpuind ) {
  286.         cpustats_mib[2]= cpuind;
  287.         size= sizeof(*st->cpustates);
  288.         memcpy(st->oldcpustates + cpuind, st->cpustates + cpuind, size);
  289.         if( sysctl(cpustats_mib, 3, st->cpustates + cpuind, &size, NULL, 0) < 0 ||
  290.             (st->cpustates[cpuind].cs_flags & CPUSTATS_ONLINE) == 0 || st->firstcall ) {
  291.             memset(st->cpus + cpuind * CPUSTATES, 0, CPUSTATES * sizeof(double));
  292.             st->cpus[cpuind*CPUSTATES+CP_IDLE]= 1.0;
  293.             continue;
  294.         }
  295.         /* Now compute fractions of CPU time spent in each state since the
  296.            previous call, using the total change as a normalisation constant */
  297.         total= 0;
  298.         for( stateind= 0; stateind < CPUSTATES; ++stateind ) {
  299.             diff= st->cpustates[cpuind].cs_time[stateind] - st->oldcpustates[cpuind].cs_time[stateind];
  300.             if( diff < 0.0 )  /* wraparound */
  301.                 diff += 0x1p64;
  302.             st->cpus[cpuind*CPUSTATES+stateind]= diff;
  303.             total += diff;
  304.         }
  305.         if( total == 0.0 )
  306.             total= 1.0;
  307.         for( stateind= 0; stateind < CPUSTATES; ++stateind )
  308.             st->cpus[cpuind*CPUSTATES+stateind] /= total;
  309.     }
  310.     size= sizeof(ue);
  311.     if( sysctl(uvmexp_mib, 2, &ue, &size, NULL, 0) < 0 ) {
  312.         st->membytes[0]= st->membytes[1]= st->membytes[2]= st->membytes[3]= 0;
  313.         st->membytes[4]= st->memtotal;
  314.         st->pagein= st->pageout= 0;
  315.         st->swapbytes[0]= st->swapbytes[1]= 0;
  316.         st->swapbytes[2]= st->swaptotal;
  317.     }
  318.     else {
  319.         st->memtotal= ue.npages * (double)ue.pagesize;
  320.         st->membytes[0]= ue.active * (double)ue.pagesize;
  321.         st->membytes[1]= ue.inactive * (double)ue.pagesize;
  322.         st->membytes[2]= ue.wired * (double)ue.pagesize;
  323.         st->membytes[4]= ue.free * (double)ue.pagesize;
  324.         st->membytes[3]= st->memtotal - st->membytes[0] - st->membytes[1] - st->membytes[2] - st->membytes[4];
  325.             /* TODO: check this */
  326.         if( st->firstcall ) {
  327.             st->pagein= 0;
  328.             st->pageout= 0;
  329.         }
  330.         else {
  331.             st->pagein= (ue.pageins - st->oldpageins) * (double)ue.pagesize;
  332.             st->pageout= (ue.pdpageouts - st->oldpageouts) * (double)ue.pagesize;
  333.         }
  334.         st->oldpageins= ue.pageins;
  335.         st->oldpageouts= ue.pdpageouts;
  336.         st->swaptotal= ue.swpages * (double)ue.pagesize;
  337.         st->swapbytes[0]= ue.swpgonly * (double)ue.pagesize;
  338.         st->swapbytes[1]= (ue.swpginuse - ue.swpgonly) * (double)ue.pagesize;
  339.         st->swapbytes[2]= (ue.swpages - ue.swpginuse) * (double)ue.pagesize;
  340.     }
  341.     size= st->tabbufsize;
  342.     while( sysctl(iflist_mib, 6, st->tabbuf, &size, NULL, 0) < 0 && errno == ENOMEM ) {
  343.         needsize= 0;
  344.         if( sysctl(iflist_mib, 6, NULL, &needsize, NULL, 0) < 0 )
  345.             break;
  346.         tryalloc= malloc(needsize);
  347.         if( ! tryalloc )
  348.             break;
  349.         free(st->tabbuf);
  350.         st->tabbuf= tryalloc;
  351.         st->tabbufsize= size= needsize;
  352.     }
  353.     st->netin= 0;
  354.     st->netout= 0;
  355.     for( off= 0; off < size - ((char*)&ifm->ifm_msglen - (char*)ifm) - sizeof(ifm->ifm_msglen); off += ifm->ifm_msglen ) {
  356.         ifm= (struct if_msghdr *)(st->tabbuf + off);
  357.         if( off + ifm->ifm_msglen > size )
  358.             break;
  359.         if( ifm->ifm_version != RTM_VERSION || ifm->ifm_type != RTM_IFINFO )
  360.             continue;
  361.         st->netin += ifm->ifm_data.ifi_ibytes;
  362.         st->netout += ifm->ifm_data.ifi_obytes;
  363.     }
  364.     if( st->firstcall ) {
  365.         st->oldtotalnetin= st->netin;
  366.         st->oldtotalnetout= st->netout;
  367.         st->netin= 0;
  368.         st->netout= 0;
  369.     }
  370.     else {
  371.         st->netin -= st->oldtotalnetin;
  372.         st->netout -= st->oldtotalnetout;
  373.         st->oldtotalnetin += st->netin;
  374.         st->oldtotalnetout += st->netout;
  375.     }
  376.     size= st->tabbufsize;
  377.     while( sysctl(diskstats_mib, 2, st->tabbuf, &size, NULL, 0) < 0 && errno == ENOMEM ) {
  378.         needsize= 0;
  379.         if( sysctl(diskstats_mib, 2, NULL, &needsize, NULL, 0) < 0 )
  380.             break;
  381.         tryalloc= malloc(needsize);
  382.         if( ! tryalloc )
  383.             break;
  384.         free(st->tabbuf);
  385.         st->tabbuf= tryalloc;
  386.         st->tabbufsize= size= needsize;
  387.     }
  388.     st->diskin= 0;
  389.     st->diskout= 0;
  390.     for( off= 0; (off+1) * sizeof(struct diskstats) <= size; ++off ) {
  391.         ds= (struct diskstats*)st->tabbuf + off;
  392.         st->diskin += ds->ds_rbytes;
  393.         st->diskout += ds->ds_wbytes;
  394.     }
  395.     if( st->firstcall ) {
  396.         st->oldtotaldiskin= st->diskin;
  397.         st->oldtotaldiskout= st->diskout;
  398.         st->diskin= 0;
  399.         st->diskout= 0;
  400.     }
  401.     else {
  402.         st->diskin -= st->oldtotaldiskin;
  403.         st->diskout -= st->oldtotaldiskout;
  404.         st->oldtotaldiskin += st->diskin;
  405.         st->oldtotaldiskout += st->diskout;
  406.     }
  407.     if( st->sensordev_psu >= 0 ) {
  408.         sensor_mib[2]= st->sensordev_psu;
  409.         sensor_mib[3]= SENSOR_INDICATOR;
  410.         sensor_mib[4]= 0;
  411.         size= sizeof(se);
  412.         if( sysctl(sensor_mib, 5, &se, &size, NULL, 0) >= 0 && size == sizeof(se) )
  413.             st->psu_on= se.value;
  414.         else
  415.             st->psu_on= 0;
  416.     }
  417.     if( st->sensordev_bat >= 0 ) {
  418.         sensor_mib[2]= st->sensordev_bat;
  419.         sensor_mib[3]= SENSOR_AMPHOUR;
  420.         sensor_mib[4]= 0;
  421.         size= sizeof(se);
  422.         if( sysctl(sensor_mib, 5, &se, &size, NULL, 0) >= 0 &&
  423.                 size == sizeof(se) && se.flags == 0 )
  424.             st->bat_max= se.value;
  425.         else
  426.             st->bat_max= 0;
  427.         sensor_mib[4]= 3;
  428.         size= sizeof(se);
  429.         if( sysctl(sensor_mib, 5, &se, &size, NULL, 0) >= 0 &&
  430.                 size == sizeof(se) && se.flags == 0 )
  431.             st->bat_curr= se.value;
  432.         else
  433.             st->bat_curr= 0;
  434.     }
  435.     st->firstcall= 0;
  436. }
  437.  
  438.  
  439. void update_filtered(obv_stats *st, int firstcall)
  440. {
  441.     double ffcoeff, fbcoeff;
  442.     int ind;
  443.  
  444.     if( firstcall ) {
  445.         fbcoeff= 0.0;
  446.         ffcoeff= 1.0;
  447.     }
  448.     else {
  449.         fbcoeff= exp2(-INTERVAL / METERDECAY2);
  450.         ffcoeff= 1.0 - fbcoeff;
  451.     }
  452.     for( ind= 0; ind< st->ncpus*CPUSTATES; ++ind )
  453.         st->filtered[ind]= fbcoeff * st->filtered[ind] + ffcoeff * st->cpus[ind];
  454.     for( ind= 0; ind< MEMSTATES; ++ind )
  455.         st->filtered[st->ncpus*CPUSTATES+ind]=
  456.             fbcoeff * st->filtered[st->ncpus*CPUSTATES+ind] + ffcoeff * st->membytes[ind];
  457.     for( ind= 0; ind< 3; ++ind )
  458.         st->filtered[st->ncpus*CPUSTATES+MEMSTATES+ind]=
  459.             fbcoeff * st->filtered[st->ncpus*CPUSTATES+MEMSTATES+ind] + ffcoeff * st->swapbytes[ind];
  460.     st->filtered[st->ncpus*CPUSTATES+MEMSTATES+3]=
  461.         fbcoeff * st->filtered[st->ncpus*CPUSTATES+MEMSTATES+3] + ffcoeff * st->pagein;
  462.     st->filtered[st->ncpus*CPUSTATES+MEMSTATES+4]=
  463.         fbcoeff * st->filtered[st->ncpus*CPUSTATES+MEMSTATES+4] + ffcoeff * st->pageout;
  464.     st->filtered[st->ncpus*CPUSTATES+MEMSTATES+5]=
  465.         fbcoeff * st->filtered[st->ncpus*CPUSTATES+MEMSTATES+5] + ffcoeff * st->diskin;
  466.     st->filtered[st->ncpus*CPUSTATES+MEMSTATES+6]=
  467.         fbcoeff * st->filtered[st->ncpus*CPUSTATES+MEMSTATES+6] + ffcoeff * st->diskout;
  468.     st->filtered[st->ncpus*CPUSTATES+MEMSTATES+7]=
  469.         fbcoeff * st->filtered[st->ncpus*CPUSTATES+MEMSTATES+7] + ffcoeff * st->netin;
  470.     st->filtered[st->ncpus*CPUSTATES+MEMSTATES+8]=
  471.         fbcoeff * st->filtered[st->ncpus*CPUSTATES+MEMSTATES+8] + ffcoeff * st->netout;
  472. }
  473.  
  474.  
  475. void init_view(obv_view *view)
  476. {
  477.     XTextProperty name;
  478.     XSizeHints *size_hints;
  479.     XWMHints *wm_hints;
  480.     XClassHint *class_hints;
  481.     XCharStruct fontextents;
  482.     char *namestr;
  483.     int screen, dummy;
  484.  
  485.     view->disp= XOpenDisplay(NULL);
  486.     if( !view->disp ) {
  487.         fprintf(stderr, "xdrawtest: Error: could not connect to X server!\n");
  488.         exit(1);
  489.     }
  490.     screen= DefaultScreen( view->disp );
  491.     view->win= XCreateSimpleWindow( view->disp, RootWindow(view->disp, screen),
  492.             view->windowx, view->windowy, view->windoww, view->windowh,
  493.             0, BlackPixel(view->disp, screen), WhitePixel(view->disp, screen) );
  494.     size_hints= XAllocSizeHints();
  495.     size_hints->flags = 0;
  496.     if( view->havepos ) {
  497.         size_hints->flags |= PPosition;
  498.         size_hints->x= view->windowx;   /* ignored in favour of create window arg */
  499.         size_hints->y= view->windowy;
  500.     }
  501.     wm_hints= XAllocWMHints();
  502.     wm_hints->flags = StateHint | InputHint;
  503.     wm_hints->initial_state= NormalState;
  504.     wm_hints->input= True;
  505.     class_hints= XAllocClassHint();
  506.     class_hints->res_name= "xobsdview";
  507.     class_hints->res_class= "xobsdview";
  508.     namestr= view->name;
  509.     XStringListToTextProperty( &namestr, 1, &name );
  510.     XSetWMProperties(view->disp, view->win, &name, &name, NULL, 0, size_hints, wm_hints, class_hints );
  511.     view->gc= XCreateGC(view->disp, view->win, 0, NULL);
  512.     view->gcval.font= XLoadFont(view->disp, FONTNAME);
  513.     if( view->gcval.font == BadName ) {
  514.         fprintf(stderr, "Could not load font %s\n", FONTNAME);
  515.         exit_view(view);
  516.         XFree(size_hints);
  517.         XFree(wm_hints);
  518.         XFree(class_hints);
  519.         exit(1);
  520.     }
  521.     else
  522.         XChangeGC(view->disp, view->gc, GCFont, &view->gcval);
  523.     XMapWindow(view->disp, view->win);
  524.     XSetBackground(view->disp, view->gc, WhitePixel(view->disp, screen));
  525.     XQueryTextExtents(view->disp, XGContextFromGC(view->gc), "N", 1, &dummy, &dummy, &dummy, &fontextents);
  526.     view->fontasc= fontextents.ascent;
  527.     view->fontdesc= fontextents.descent;
  528.     view->fontw= fontextents.width;
  529.     view->fontgap= view->fontw / 2;
  530.     XFree(size_hints);
  531.     XFree(wm_hints);
  532.     XFree(class_hints);
  533. }
  534.  
  535.  
  536. void resize_view(obv_view *view, unsigned ncpus)
  537. {
  538.     char cpustr[6];
  539.     const char *label;
  540.     int nmeters, ind, y;
  541.  
  542.     nmeters= ncpus + 7;
  543.     view->meteroff= (view->windowh - PADDING) / nmeters;
  544.     if( view->meteroff < METERMINHEIGHT + 2 + view->fontasc + view->fontdesc + 1 + 4 ) {
  545.         view->meteroff= METERMINHEIGHT + 2 + view->fontasc + view->fontdesc + 1 + 4;
  546.         view->meterh= METERMINHEIGHT;
  547.     }
  548.     else
  549.         view->meterh= view->meteroff - (2 + view->fontasc + view->fontdesc + 1 + 4);
  550.     view->labelx= PADDING;
  551.     view->valuex= view->labelx + 5*view->fontw;
  552.     view->meterx= view->valuex + 4*view->fontw + view->fontgap;
  553.     view->meterw= view->windoww - PADDING - 1 - view->meterx;
  554.     if( view->meterw < METERMINWIDTH )
  555.         view->meterw= METERMINWIDTH;
  556.     view->metery= PADDING + view->fontasc + view->fontdesc + 3;
  557.     view->label0y= view->metery + (view->meterh - view->fontasc - view->fontdesc) / 2 + view->fontasc;
  558.  
  559.     XSetForeground(view->disp, view->gc, 0xFFFFFF);
  560.     XFillRectangle(view->disp, view->win, view->gc, 0, 0, view->windoww, view->windowh);
  561.     XSetForeground(view->disp, view->gc, 0);
  562.     y= view->metery - 1;
  563.     for( ind= 0; ind< ncpus+7; ++ind, y += view->meteroff )
  564.         XDrawRectangle(view->disp, view->win, view->gc, view->meterx-1, y, view->meterw+1, view->meterh+1);
  565.     y= view->label0y;
  566.     XDrawString(view->disp, view->win, view->gc, view->labelx, y, "LOAD", 4);
  567.     y += view->meteroff;
  568.     for( ind= 0; ind< ncpus; ++ind ) {
  569.         snprintf(cpustr, 6, "CPU%d", ind);
  570.         XDrawString(view->disp, view->win, view->gc, view->labelx, y, cpustr, 4 + (ind > 9));
  571.         y += view->meteroff;
  572.     }
  573.     XDrawString(view->disp, view->win, view->gc, view->labelx, y, "MEM", 3);
  574.     y += view->meteroff;
  575.     XDrawString(view->disp, view->win, view->gc, view->labelx, y, "SWAP", 4);
  576.     y += view->meteroff;
  577.     XDrawString(view->disp, view->win, view->gc, view->labelx, y, "PAGE", 4);
  578.     y += view->meteroff;
  579.     XDrawString(view->disp, view->win, view->gc, view->labelx, y, "DISK", 4);
  580.     y += view->meteroff;
  581.     XDrawString(view->disp, view->win, view->gc, view->labelx, y, "NET", 3);
  582.     y += view->meteroff;
  583.     XDrawString(view->disp, view->win, view->gc, view->labelx, y, "BAT", 3);
  584.     label= "PROCS";
  585.     draw_key(view, 0, &label, warncol, 1);
  586.     for( ind= 0; ind< ncpus; ++ind )
  587.         draw_key(view, ind+1, cpustabbr, cpustcol, CPUSTATES);
  588.     draw_key(view, ncpus+1, memabbr, memcol, MEMSTATES);
  589.     draw_key(view, ncpus+2, swapabbr, swapcol, 3);
  590.     draw_key(view, ncpus+3, ioabbr, iocol, 3);
  591.     draw_key(view, ncpus+4, diskabbr, iocol, 3);
  592.     draw_key(view, ncpus+5, ioabbr, iocol, 3);
  593.     label= "CHARGE";
  594.     draw_key(view, ncpus+6, &label, warncol, 1);
  595. }
  596.  
  597.  
  598. void update_view(obv_view *view, obv_stats *st)
  599. {
  600.     double values[MEMSTATES+3];
  601.     int ind;
  602.  
  603.     draw_warnmeter(view, 0, !!METERSPLIT, st->loadavg[0]/MAXLOAD, 0);
  604.     draw_value(view, 0, 0, st->loadavg[0]);
  605.     if( METERSPLIT )
  606.         draw_warnmeter(view, 0, 2, st->loadavg[1]/MAXLOAD, 0);
  607.     for( ind= 0; ind< st->ncpus; ++ind ) {
  608.         draw_meter(view, ind+1, !!METERSPLIT, st->cpus + ind*CPUSTATES, cpustcol, CPUSTATES);
  609.         draw_value(view, ind+1, 1, 1.0 - st->cpus[(ind+1)*CPUSTATES-1]);
  610.         if( METERSPLIT )
  611.             draw_meter(view, ind+1, 2, st->filtered + ind*CPUSTATES, cpustcol, CPUSTATES);
  612.     }
  613.     for( ind= 0; ind< MEMSTATES; ++ind )
  614.         values[ind]= st->membytes[ind] / st->memtotal;
  615.     draw_meter(view, st->ncpus+1, !!METERSPLIT, values, memcol, MEMSTATES);
  616.     draw_value(view, st->ncpus+1, 2, st->memtotal - st->membytes[MEMSTATES-2] - st->membytes[MEMSTATES-1]);
  617.     if( METERSPLIT ) {
  618.         for( ind= 0; ind< MEMSTATES; ++ind )
  619.             values[ind]= st->filtered[st->ncpus*CPUSTATES+ind] / st->memtotal;
  620.         draw_meter(view, st->ncpus+1, 2, values, memcol, MEMSTATES);
  621.     }
  622.     for( ind= 0; ind< 3; ++ind )
  623.         values[ind]= st->swapbytes[ind] / st->swaptotal;
  624.     draw_meter(view, st->ncpus+2, !!METERSPLIT, values, swapcol, 3);
  625.     draw_value(view, st->ncpus+2, 2, st->swapbytes[0] + st->swapbytes[1]);
  626.     if( METERSPLIT ) {
  627.         for( ind= 0; ind< 3; ++ind )
  628.             values[ind]= st->filtered[st->ncpus*CPUSTATES+MEMSTATES+ind] / st->swaptotal;
  629.         draw_meter(view, st->ncpus+2, 2, values, swapcol, 3);
  630.     }
  631.     values[0]= st->pagein / INTERVAL / MAXDISKBW;
  632.     values[1]= st->pageout / INTERVAL / MAXDISKBW;
  633.     draw_meter(view, st->ncpus+3, !!METERSPLIT, values, iocol, 3);
  634.     draw_value(view, st->ncpus+3, 2, (st->pagein + st->pageout) / INTERVAL);
  635.     if( METERSPLIT ) {
  636.         values[0]= st->filtered[st->ncpus*CPUSTATES+MEMSTATES+3] / INTERVAL / MAXDISKBW;
  637.         values[1]= st->filtered[st->ncpus*CPUSTATES+MEMSTATES+4] / INTERVAL / MAXDISKBW;
  638.         draw_meter(view, st->ncpus+3, 2, values, iocol, 3);
  639.     }
  640.     values[0]= (double)st->diskin / INTERVAL / MAXDISKBW;
  641.     values[1]= (double)st->diskout / INTERVAL / MAXDISKBW;
  642.     draw_meter(view, st->ncpus+4, !!METERSPLIT, values, iocol, 3);
  643.     draw_value(view, st->ncpus+4, 2, (double)(st->diskin + st->diskout) / INTERVAL);
  644.     if( METERSPLIT ) {
  645.         values[0]= st->filtered[st->ncpus*CPUSTATES+MEMSTATES+5] / INTERVAL / MAXDISKBW;
  646.         values[1]= st->filtered[st->ncpus*CPUSTATES+MEMSTATES+6] / INTERVAL / MAXDISKBW;
  647.         draw_meter(view, st->ncpus+4, 2, values, iocol, 3);
  648.     }
  649.     values[0]= (double)st->netin / INTERVAL / MAXNETBW;
  650.     values[1]= (double)st->netout / INTERVAL / MAXNETBW;
  651.     draw_meter(view, st->ncpus+5, !!METERSPLIT, values, iocol, 3);
  652.     draw_value(view, st->ncpus+5, 2, (double)(st->netin + st->netout) / INTERVAL);
  653.     if( METERSPLIT ) {
  654.         values[0]= st->filtered[st->ncpus*CPUSTATES+MEMSTATES+7] / INTERVAL / MAXNETBW;
  655.         values[1]= st->filtered[st->ncpus*CPUSTATES+MEMSTATES+8] / INTERVAL / MAXNETBW;
  656.         draw_meter(view, st->ncpus+5, 2, values, iocol, 3);
  657.     }
  658.     if( st->bat_max > 0 ) {
  659.         values[0]= (double)st->bat_curr / st->bat_max;
  660.         if( st->psu_on )
  661.             draw_meter(view, st->ncpus+6, 0, values, chargingcol, 2);
  662.         else
  663.             draw_warnmeter(view, st->ncpus+6, 0, values[0], 1);
  664.         draw_value(view, st->ncpus+6, 1, values[0]);
  665.     }
  666. }
  667.  
  668.  
  669. void exit_view(obv_view *view)
  670. {
  671.     XUnmapWindow(view->disp, view->win);
  672.     XUnloadFont(view->disp, view->gcval.font);
  673.     XFreeGC(view->disp, view->gc);
  674.     XCloseDisplay(view->disp);
  675. }
  676.  
  677.  
  678. /* Draw horizontally stacked coloured bars of a meter of multiple values that
  679.    add up to 1.0.  The meter index determines the y position.  If split is 1,
  680.    only the upper half of the bar is painted; if it is 2, only the lower half.
  681.    The last 3 arguments give the values and colours.  The last value is
  682.    ignored; its bar is filled in to the right end of the meter.  */
  683. void draw_meter(obv_view *view, unsigned metind, int split, const double *fractions, const uint32_t *colours, unsigned n)
  684. {
  685.     double sum;
  686.     unsigned y, h, xoff, xtop, ind;
  687.  
  688.     y= view->metery + metind * view->meteroff;
  689.     if( split ) {
  690.         h= (view->meterh + 1) / 2;
  691.         if( split == 2 ) {
  692.             y += h;
  693.             if( view->meterh & 1 )
  694.                 --h;
  695.         }
  696.     }
  697.     else
  698.         h= view->meterh;
  699.     xoff= 0;
  700.     sum= 0.0;
  701.     for( ind= 0; ind < n-1; ++ind ) {
  702.         if( fractions[ind] <= 0.0 )
  703.             continue;
  704.         sum += fractions[ind];
  705.         if( sum > 1.0 )
  706.             sum= 1.0;
  707.         xtop= (unsigned)(view->meterw * sum + 0.5);
  708.         if( xtop <= xoff )
  709.             continue;
  710.         XSetForeground(view->disp, view->gc, colours[ind]);
  711.         XFillRectangle(view->disp, view->win, view->gc, view->meterx+xoff, y, xtop-xoff, h);
  712.         xoff= xtop;
  713.         if( xoff >= view->meterw )
  714.             return;
  715.     }
  716.     XSetForeground(view->disp, view->gc, colours[n-1]);
  717.     XFillRectangle(view->disp, view->win, view->gc, view->meterx+xoff, y, view->meterw-xoff, h);
  718. }
  719.  
  720.  
  721. /* Draw a horizontal bar indicating a single value, in a colour also indicating
  722.    the value.  The meter index determines the y position.  If split is 1, only
  723.    the upper half of the bar is painted; if it is 2, only the lower half.  The
  724.    colour is interpolated from warncol[] for values between 0.1 and 0.9. */
  725. void draw_warnmeter(obv_view *view, unsigned metind, int split, double value, int reverse)
  726. {
  727.     double colfrac;
  728.     uint32_t colour;
  729.     unsigned y, h;
  730.     int w, ind, colind;
  731.  
  732.     y= view->metery + metind * view->meteroff;
  733.     if( split ) {
  734.         h= (view->meterh + 1) / 2;
  735.         if( split == 2 ) {
  736.             y += h;
  737.             if( view->meterh & 1 )
  738.                 --h;
  739.         }
  740.     }
  741.     else
  742.         h= view->meterh;
  743.     w= value * view->meterw + 0.5;
  744.     if( w < 0 )
  745.         w= 0;
  746.     else if( w > view->meterw )
  747.         w= view->meterw;
  748.     if( reverse )
  749.         value= 1.0 - value;
  750.     colfrac= (value - 0.1) / 0.8 * (NWARNCOL - 1);
  751.     if( colfrac <= 0.0 )
  752.         colour= warncol[0];
  753.     else if( colfrac >= NWARNCOL-1 )
  754.         colour= warncol[NWARNCOL-1];
  755.     else {
  756.         colind= (int)colfrac;
  757.         colfrac -= colind;
  758.         for( ind= 0; ind< 4; ++ind )
  759.             ((uint8_t*)&colour)[ind]= colfrac * ((uint8_t*)(warncol+colind+1))[ind] + (1.0 - colfrac) * ((uint8_t*)(warncol+colind))[ind];
  760.     }
  761.     XSetForeground(view->disp, view->gc, colour);
  762.     XFillRectangle(view->disp, view->win, view->gc, view->meterx, y, w, h);
  763.     if( w < view->meterw ) {
  764.         XSetForeground(view->disp, view->gc, IDLECOL);
  765.         XFillRectangle(view->disp, view->win, view->gc, view->meterx + w, y, view->meterw - w, h);
  766.     }
  767. }
  768.  
  769.  
  770. /* Draw value as text to the left of meter.  type is 0 for numeric, 1 for
  771.    percentage, 2 for data volume.  */
  772. void draw_value(obv_view *view, unsigned metind, int type, double value)
  773. {
  774.     char valstr[5];
  775.  
  776.     if( type == 0 )
  777.         snprintf(valstr, 5, "%4g", value);
  778.     else if( type == 1 )
  779.         snprintf(valstr, 5, "%3d%%", (int)(100*value+0.5));
  780.     else
  781.         sprintbytes(valstr, value);
  782.     XSetForeground(view->disp, view->gc, VALUECOL);
  783.     XDrawImageString(view->disp, view->win, view->gc, view->valuex, view->label0y + metind*view->meteroff, valstr, 4);
  784. }
  785.  
  786.  
  787. /* Draw a concatenated string in multiple colours above the meter with the
  788.    given index. */
  789. void draw_key(obv_view *view, unsigned metind, const char **strs, const uint32_t *colours, int n)
  790. {
  791.     int x, y, w, ind, xoff, nchars, dummy;
  792.  
  793.     w= 0;
  794.     for( ind= 0; ind < n; ++ind )
  795.         w += strlen(strs[ind]);
  796.     w= w * view->fontw + (n - 1) * view->fontgap;
  797.     x= view->meterx;
  798.     if( w > view->meterw )
  799.         x -= w - view->meterw;
  800.     y= view->metery + metind * view->meteroff - 3 - view->fontdesc;
  801.     for( ind= 0; ind < n; ++ind ) {
  802.         nchars= strlen(strs[ind]);
  803.         XSetForeground(view->disp, view->gc, colours[ind]);
  804.         XDrawString(view->disp, view->win, view->gc, x, y, strs[ind], nchars);
  805.         x += view->fontw * nchars;
  806.         x += view->fontgap;
  807.     }
  808. }
  809.  
  810.  
  811. /* Print abbreviated byte count in 5 chars including terminating 0. */
  812. static const char suffix[]= "kMGTPE";
  813. void sprintbytes(char *dest, double bytes)
  814. {
  815.     int tenbits;
  816.  
  817.     tenbits= bytes > 0.0 ? (int)floor(log2(bytes)/10) : 0;
  818.     bytes /= exp2(10.0 * tenbits);
  819.     if( bytes < 10 )
  820.         snprintf(dest, 4, "%.1f", bytes);
  821.     else if( bytes < 100 )
  822.         snprintf(dest, 4, " %d", (int)floor(bytes));
  823.     else
  824.         snprintf(dest, 4, "%d", (int)floor(bytes));
  825.     if( tenbits == 0 )
  826.         dest[3]= ' ';
  827.     else if( tenbits < 7 )
  828.         dest[3]= suffix[tenbits-1];
  829.     else
  830.         dest[3]= '!';
  831.     dest[4]= 0;
  832. }
Tags: openbsd
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement