Single screen Stats and Top apps for the G15:
[attachment=2]g15status.jpg[/attachment] [attachment=1]g15top.jpg[/attachment]
Source code is attached. Both programs use g15composer and load it automatically. Feel free to use them or modify as you see fit!
Re: Status and Top utils
It's good to see it getting some use! Thanks for the changes.
Re: Status and Top utils
Hello Biffidus, I found g15status really useful and I prefer it to g15stats.
I took the liberty of making some changes to suit my needs:
Here's a picture:

I also fixed a small bug in update_procinfo() that did not display the 5 minute load average: you have used twice the variable fifteenminute
#include // printf, fprintf, fscanf, fopen, fclose<br /> #include // sleep, close<br /> #include // strncpy<br /> #include // strftime<br /> #include // statvfs<br /> #include // socket<br /> #include // ioctl<br /> #include // ifreq<br /> #include // in_addr<br /> #include // inet_ntoa<br /> #include // vsnprintf<br /> #include<br /> #include </p> <p>int screen; // g15 screen device<br /> g15canvas canvas, empty_canvas; // g15 canvas</p> <p>#define CLOCKLEN 22 // length of clock buffer<br /> #define LINELEN 256 // length of line buffer</p> <p>void g15printf(g15canvas *canvas, int x, int y, const char *format, ...)<br /> {<br /> static char line[LINELEN];<br /> va_list ap;<br /> // print into the buffer<br /> va_start(ap, format);<br /> vsnprintf(line, LINELEN, format, ap);<br /> va_end(ap);<br /> // print to the canvas<br /> g15r_renderString(canvas, line, 0, G15_TEXT_SMALL, x, y);<br /> }</p> <p>/* UPDATE_CLOCK: returns seconds until next change */</p> <p>int update_clock ()<br /> {<br /> const char *format = "%a %d %b, %H:%M:%S"; // format of date output<br /> char clockbuf[CLOCKLEN]; // buffer for clock string<br /> time_t time_now; struct tm tm_now; // current time </p> <p> time(&time_now);<br /> if (localtime_r(&time_now, &tm_now) != NULL &&<br /> strftime(clockbuf, CLOCKLEN, format, &tm_now) == 20)<br /> g15r_renderString(&canvas, clockbuf, 0, G15_TEXT_LARGE, 0, 0);</p> <p> return 60 - tm_now.tm_sec;<br /> }</p> <p>/* UPDATE_PROCINFO */</p> <p>void update_procinfo ()<br /> {<br /> FILE *F;<br /> double oneminute, fiveminute, fifteenminute; // 1,5 & 15 minute load averages<br /> int running, total = 0; // number of running processes<br /> int uptime = 0; // uptime<br /> int thrm; // current CPU temperature<br /> // read data from /proc<br /> if ((F = fopen("/proc/loadavg", "r")) != NULL)<br /> {<br /> fscanf(F, "%lf %lf %lf %u/%u ", &oneminute, &fiveminute,<br /> &fifteenminute, &running, &total);<br /> fclose(F);<br /> }<br /> if ((F = fopen("/proc/uptime", "r")) != NULL)<br /> {<br /> fscanf(F, "%u.", &uptime);<br /> fclose(F);<br /> }<br /> if (total && uptime) // output result<br /> {<br /> int days = uptime / (60 * 60 * 24);<br /> int hours = uptime / (60 * 60) % 24;<br /> int minutes = uptime / 60 % 60;<br /> char clockbuf[CLOCKLEN];</p> <p> if (days > 1)<br /> snprintf(clockbuf, CLOCKLEN, "%u days %u:%02u", days, hours, minutes);<br /> else if (days == 1)<br /> snprintf(clockbuf, CLOCKLEN, "%u day %u:%02u", days, hours, minutes);<br /> else<br /> snprintf(clockbuf, CLOCKLEN, "%u:%02u", hours, minutes);</p> <p> g15printf(&canvas, 23, 13, "%2.2f, %2.2f, %2.2f up %s",<br /> oneminute, fiveminute, fifteenminute, clockbuf);<br /> g15printf(&canvas, 23, 19, "%u total, %u running", total, running);<br /> }<br /> if ((F = fopen("/proc/acpi/thermal_zone/THRM/temperature", "r")) != NULL)<br /> {<br /> fscanf(F, "%*s%d", &thrm);<br /> g15printf(&canvas, 133, 19, "CPU %dC",thrm);<br /> fclose(F);<br /> }</p> <p>}</p> <p>/* UPDATE_MEMORY */</p> <p>void update_memory()<br /> {<br /> FILE *F;<br /> int memtotal = 0, memfree = 0, buffers = 0, cached = 0, memused = 0;</p> <p> if ((F = fopen("/proc/meminfo", "r")) != NULL)<br /> {<br /> fscanf(F, "MemTotal: %u kB\n", &memtotal);<br /> fscanf(F, "MemFree: %u kB\n", &memfree);<br /> fscanf(F, "Buffers: %u kB\n", &buffers);<br /> fscanf(F, "Cached: %u kB\n", &cached);<br /> fclose(F);<br /> }<br /> // calculate memory used by programs<br /> memused = memtotal - memfree - buffers - cached;<br /> if (memtotal > memused && memused > 0)<br /> g15printf(&canvas, 23, 25,"%dMB/%dMB", memused / 1024, memtotal / 1024);<br /> g15r_drawBar(&canvas, 84, 27, 159, 28, 1, memused, memtotal, 1);</p> <p>}</p> <p>/* UPDATE_DISK */</p> <p>void update_disk ()<br /> {<br /> struct statvfs s; // filesystem information for /<br /> if (statvfs("/home/", &s) != -1)<br /> g15printf(&canvas, 23, 31,"%.1lfGB/%.1lfGB",<br /> (double)(s.f_blocks - s.f_bavail) * s.f_bsize / (1024 * 1024 * 1024),<br /> (double)s.f_blocks * s.f_bsize / (1024 * 1024 * 1024));<br /> g15r_drawBar(&canvas, 84, 33, 159, 34, 1,<br /> s.f_blocks - s.f_bavail, s.f_blocks, 1);<br /> }</p> <p>/* UPDATE_NET */</p> <p>char *gateway_dev (void)<br /> {<br /> FILE *F;<br /> static char linebuf[LINELEN];<br /> int destination, gateway;</p> <p> // scan for the default route with gateway set and destination ip = 0.0.0.0<br /> if ((F = fopen("/proc/net/route", "r")) != NULL)<br /> {<br /> while (fgets(linebuf, LINELEN, F))<br /> if (fscanf(F, "%s %x %x 00", linebuf, &destination, &gateway) == 3<br /> && destination == 0 && gateway != 0)<br /> {<br /> fclose(F);<br /> return linebuf;<br /> }<br /> fclose(F);<br /> }<br /> return NULL;<br /> }</p> <p>char *get_ipaddr (const char *dev)<br /> {<br /> char *result = NULL;<br /> struct ifreq ifr;<br /> int fd;</p> <p> // open a socket and use ioctl to query the source ip address<br /> if (dev && (fd = socket(PF_INET, SOCK_DGRAM, 0)) != -1)<br /> {<br /> strncpy(ifr.ifr_name, dev, IFNAMSIZ);<br /> if (! ioctl(fd, SIOCGIFADDR, &ifr))<br /> result = inet_ntoa(((struct sockaddr_in *)&(ifr.ifr_ifru.ifru_addr))<br /> ->sin_addr);<br /> close(fd);<br /> }<br /> return result;<br /> }</p> <p>char *get_wireless_info (const char *dev)<br /> {<br /> FILE *F;<br /> static char linebuf[LINELEN];<br /> int link, level;</p> <p> if (dev && (F = fopen("/proc/net/wireless", "r")) != NULL)<br /> {<br /> while (fgets(linebuf, LINELEN, F))<br /> if (fscanf(F, "%s %*x %d%*c %d%*c", linebuf, &link, &level) == 3<br /> && strncmp(dev, linebuf, strlen(dev)) == 0)<br /> {<br /> snprintf(linebuf, 200, "%d%% %ddBm", link, level);<br /> fclose(F);<br /> return linebuf;<br /> }<br /> fclose(F);<br /> }<br /> return NULL;<br /> }</p> <p>void update_net()<br /> {<br /> char *dev, *ipaddr, *winfo;</p> <p> dev = gateway_dev();<br /> ipaddr = get_ipaddr(dev);<br /> winfo = get_wireless_info(dev);</p> <p> g15printf(&canvas, 23, 37, "%s %s %s",<br /> dev ? dev : "no default route", // device name or error message<br /> ipaddr ? ipaddr : "", // ip address if available<br /> winfo ? winfo : ""); // wireless info if available<br /> }</p> <p>int main (int argc, char **argv)<br /> {<br /> int delay;</p> <p> if ((screen = new_g15_screen(G15_G15RBUF)) < 0)<br /> {<br /> fprintf(stderr, "Unable to connect to G15daemon\n");<br /> return 1;<br /> }<br /> g15r_initCanvas(&empty_canvas);<br /> g15r_clearScreen(&empty_canvas, G15_COLOR_WHITE);<br /> g15r_drawLine(&empty_canvas, 0, 9, 159, 9, 1);<br /> g15printf(&empty_canvas, 2, 13, "load:");<br /> g15printf(&empty_canvas, 2, 19, "task:");<br /> g15printf(&empty_canvas, 2, 25, " ram:");<br /> g15printf(&empty_canvas, 2, 31, "home:");<br /> g15printf(&empty_canvas, 2, 37, " net:");</p> <p> // It's business time!<br /> while (1)<br /> {<br /> // clear canvas<br /> memcpy(&canvas, &empty_canvas, sizeof(g15canvas));</p> <p> // fill in the blanks<br /> update_procinfo();<br /> update_memory();<br /> update_disk();<br /> update_net();<br /> delay = update_clock();</p> <p> g15_send(screen, (char*)canvas.buffer, G15_BUFFER_LEN);</p> <p> // sleep for 5 seconds or until the clock needs to be updated<br /> sleep ( delay < 5 ? delay : 5);<br /> }<br /> }<br />[/]Re: Status and Top utils
Heh, no worries.
I'm loving having the clock and stats available all the time, especially the wireless signal strength ;)
Re: Status and Top utils
Its really great, thanks :)
Re: Status and Top utils
libg15render version uploaded.
Hrm, I didn't see your message until I came back to upload my changes. I'm not using any of the TTf calls but if I have issues I'll replace the initCanvas call with a memset ;)
Re: Status and Top utils
It does indeed work now. Thanks. The man page for g15daemon_client_devel has the details for sending data to g15daemon. It is pretty much as SteelSide described. The testlibg15render directory in the g15tools SVN repo also has a simple example tying it all together. Here are the functions from there that connect to g15daemon and send a screen:
void<br /> updateScreen (void)<br /> {<br /> if (!canvas->mode_cache)<br /> g15_send (g15screen_fd, (char *) canvas->buffer, 1048);<br /> }</p> <p>void<br /> connectToScreen (void)<br /> {<br /> if ((g15screen_fd = new_g15_screen (G15_G15RBUF)) < 0)<br /> {<br /> cout << "Sorry, cant connect to the G15daemon" << endl;<br /> exit (-1);<br /> }</p> <p> canvas = (g15canvas *) malloc (sizeof (g15canvas));</p> <p> g15r_initCanvas (canvas);<br /> }There are potential issues with g15r_initCanvas() related to how TTF_SUPPORT is handled, so I'd recommend using calloc() rather than malloc() and skipping g15r_initCanvas().
Re: Status and Top utils
It's very simple infact, i can give you a link to a c++ piece i worked on.
(& it is in the manpage)
What you want to useis the g15_send() function, which you should pass
screenfd, w/e array you use for your canvas, then access the buffer element of it and G15_BUFFER_LEN.
(Man pages describes it alot better than i :>)
I recommend that you check source of g15stats to see how it does it. (That's how i learned)
http://openlierox.svn.sourceforge.net/viewvc/openlierox/src/client/OLXG1...
Re: Status and Top utils
Ok, I've made g15top a bit smarter - it scans the colum headings to figure out where the %CPU, %MEM and COMMANDS are located - does it work for you now?
I've also fixed a bug in g15status that was causing it to fail if no internet connection was present.
Do you have any simple examples of the libg15render library in action? Its manpage only describes how to use it to populate the buffer but not how to send it to the LCD!
Re: Status and Top utils
g15composer is essentially a wrapper around libg15render to allow scripting languages to interact with the LCD. Since your program is already written in C, there is no need for a wrapper and you can avoid loading an extra program and passing the data over a pipe. By using libg15render/g15daemon you would just open a socket connection to g15daemon, build the buffer with libg15render methods that are pretty much a 1:1 mapping of the g15composer commands you already have, and send that buffer to g15daemon for display.
Re: Status and Top utils
Thanks!
It looks like your top output has the columns in a different order, I'll see if I can make the parser a little smarter and then upload a new version...
I wrote them both as quick/simple apps so I could pipe the output to G15composer (which is a great app and is very easy to debug). I then made them load top/g15composer themselves because the script I wrote to do it wasn't 100% reliable. Are there any benefits to using the library over g15comopser?
My top output looks like this:
top - 14:40:59 up 20 min, 2 users, load average: 0.01, 0.02, 0.00<br /> Tasks: 95 total, 1 running, 94 sleeping, 0 stopped, 0 zombie<br /> Cpu(s): 1.4%us, 0.4%sy, 0.0%ni, 97.2%id, 0.8%wa, 0.0%hi, 0.1%si, 0.0%st<br /> Mem: 2061416k total, 329100k used, 1732316k free, 26568k buffers<br /> Swap: 2008116k total, 0k used, 2008116k free, 153380k cached</p> <p> PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND<br /> 1 root 20 0 3724 584 484 S 0 0.0 0:00.26 init<br /> 2 root 15 -5 0 0 0 S 0 0.0 0:00.00 kthreadd<br /> 3 root RT -5 0 0 0 S 0 0.0 0:00.00 migration/0Re: Status and Top utils
Very cool. lcdproc gives me most of the info in g15status, but not g15top which would be nice to have but it doesn't seem to work for me. Here's the head from top.
top - 23:31:40 up 3 days, 1:20, 4 users, load average: 0.19, 0.21, 0.25<br /> Tasks: 277 total, 1 running, 274 sleeping, 2 stopped, 0 zombie<br /> Cpu(s): 1.8%us, 0.4%sy, 0.3%ni, 96.9%id, 0.5%wa, 0.0%hi, 0.1%si, 0.0%st<br /> Mem: 8183136k total, 7322764k used, 860372k free, 317076k buffers<br /> Swap: 6312948k total, 436k used, 6312512k free, 5754952k cached</p> <p> PID PPID TIME+ %CPU %MEM PR NI S VIRT SWAP RES UID COMMAND<br /> 1 0 479:05.11 0 0.0 20 0 S 3716 3132 584 0 init [3]<br /> 21318 21315 98:45.86 0 0.1 20 0 S 41920 36m 4960 0 -su<br /> 27495 27492 82:55.45 0 0.1 20 0 S 42512 36m 5648 0 -suI've got to wonder though, since you're already using C why use g15composer rather that g15daemon and libg15render? The lcdproc driver and g15composer or any of the other clients using libg15render should give you a start on using the libraries if you need examples.
Re: Status and Top utils
Oh, I guess I should add that they've been tested on a 64-bit gentoo system and an old pentium running Ubuntu.
g15status grabs it's data from system calls and files in the /proc filesystem. If any of these fail, that section will be left blank. The memory bar ignores buffers/cache and only reports memory that in use by processes. The disk bar reports the usage of the root filesystem.
g15top uses batch-mode top output. If this doesn't work then the output from your top command may differ from mine, so post the first 10 lines (top -b -n 1 | head -n 10) and I'll see if I can make it work for both! The top cpu% output is limited to integer values on 64-bit systems so it won't report processes under 1%.