diff --git a/pywmdatetime/INSTALL b/pywmdatetime/INSTALL new file mode 100644 index 0000000..c99f19b --- /dev/null +++ b/pywmdatetime/INSTALL @@ -0,0 +1,8 @@ +1. If you don't have pywmgeneral installed, install it first. Can be found + at http://foo.unix.se/pywmdockapps + +2. Copy pywmdatetime.py to somewhere in your path (like ~/bin or + /usr/local/bin). + +3. Copy the sample.pywmdatetimerc to ~/.pywmdatetimerc and edit as you like. + diff --git a/pywmdatetime/README b/pywmdatetime/README new file mode 100644 index 0000000..adc23fc --- /dev/null +++ b/pywmdatetime/README @@ -0,0 +1,15 @@ +[WHAT] +Pywmdatetime is a WindowMaker dockapp for displaying time, date and +some other information. The colors and formats are easy to configure +through the configuration file or as command line arguments. Invoke +the program with --help or see the sample rc-file for more information. + +[WHY] +I made it mostly because there was no time displaying app that suited my +needs. I also made it to try out my newly written Python module for that +provides helping functions for making dockapps. + +[CONTACT] +Mail anything related to this program to me, Kristoffer Erlandsson, + + diff --git a/pywmdatetime/pywmdatetime.py b/pywmdatetime/pywmdatetime.py new file mode 100755 index 0000000..9b5452e --- /dev/null +++ b/pywmdatetime/pywmdatetime.py @@ -0,0 +1,346 @@ +#!/usr/bin/env python + +'''pywmdatetime.py + +WindowMaker dockapp that displays time, date, weekday and week number. + +Copyright (C) 2003 Kristoffer Erlandsson + +Licensed under the GNU General Public License. + + +Changes: +2003-06-24 Kristoffer Erlandsson +Added event handling for graceful shutdown + +2003-06-16 Kristoffer Erlandsson +First workingish version + +''' +usage = '''pywmdatetime.py [options] +Available options are: +-h, --help print this help +-f, --foreground set the foreground color +-b, --background set the background color +-t, --timeformat set the time format +-d, --dateformat set the date format +-y, --weekdayformat set the weekday format +-e, --weekformat set the week format +-r, --rgbfile set the rgb file to get color codes from +-c, --configfile set the config file to use + +The formats are the same as Python's strftime() accept. See the sample +rc-file for more information about this. +''' + +import sys +import time +import getopt +import os + +import pywmhelpers + +width = 64 +height = 64 + +lettersStartX = 0 +lettersStartY = 74 +letterWidth = 6 +letterHeight = 8 + +digitsStartX = 0 +digitsStartY = 64 +digitWidth = 6 +digitHeight = 8 + +xOffset = 4 +yOffset = 4 + +letters = 'abcdefghijklmnopqrstuvwxyz' +digits = '0123456789:/- ' + +timeDefaultFormat = '%H:%M:%S' +dateDefaultFormat = '%d-%m-%y' +dayDefaultFormat = '%A' +weekDefaultFormat = 'wk %q' # %q added by me for different week calculation. + +defaultConfigFile = '~/.pywmdatetimerc' +defaultRGBFiles = ['/usr/lib/X11/rgb.txt', '/usr/X11R6/lib/X11/rgb.txt'] +maxCharsPerLine = 9 + +def addString(s, x, y): + try: + pywmhelpers.addString(s, x, y, letterWidth, letterHeight, lettersStartX, + lettersStartY, letters, digitWidth, digitHeight, + digitsStartX, digitsStartY, digits, xOffset, yOffset, + width, height) + except ValueError, e: + sys.stderr.write('Error when painting string:\n' + str(e) + '\n') + sys.exit(3) + +def getCenterStartPos(s): + return pywmhelpers.getCenterStartPos(s, letterWidth, width, xOffset) + +def getVertSpacing(numLines, margin): + return pywmhelpers.getVertSpacing(numLines, margin, height, letterHeight, + yOffset) + +def calculateWeek(localTime): + '''Calculate the week number as we do, for example in Sweden. + + That is, add one to the %W format if the year didn't start on a monday.''' + day = int(time.strftime('%j', localTime)) + weekDay = int(time.strftime('%w')) - 1 + if weekDay == -1: + weekDay = 6 + lastMonday = day - weekDay + if lastMonday % 7 == 0: + return int(time.strftime('%W')) + return int(time.strftime('%W')) + 1 + +def parseCommandLine(argv): + '''Parse the commandline. Return a dictionary with options and values.''' + shorts = 'hf:b:t:d:e:y:r:c:' + longs = ['help', 'foreground=', 'background=', 'timeformat=', 'dateformat=', + 'weekdayformat=', 'weekformat=', 'rgbfile=', 'configfile='] + try: + opts, nonOptArgs = getopt.getopt(argv[1:], shorts, longs) + except getopt.GetoptError, e: + sys.stderr.write('Error when parsing commandline: ' + str(e) + '\n') + sys.stderr.write(usage) + sys.exit(2) + d = {} + for o, a in opts: + if o in ('-h', '--help'): + sys.stdout.write(usage) + sys.exit(0) + if o in ('-f', '--foreground'): + d['foreground'] = a + if o in ('-b', '--background'): + d['background'] = a + if o in ('-t', '--timeformat'): + d['timeformat'] = a + if o in ('-d', '--dateformat'): + d['dateformat'] = a + if o in ('-y', '--weekdayformat'): + d['weekdayformat'] = a + if o in ('-e', '--weekformat'): + d['weekformat'] = a + if o in ('-r', '--rgbfile'): + d['rgbfile'] = a + if o in ('-c', '--configfile'): + d['configfile'] = a + return d + +def checkForEvents(): + event = pywmhelpers.getEvent() + while not event is None: + if event['type'] == 'destroynotify': + sys.exit(0) + +def mainLoop(timeFmt, dateFmt, dayFmt, weekFmt): + recalcWeek = weekFmt.find('%q') + 1 # True if we found %q. + counter = -1 + while 1: + counter += 1 + checkForEvents() + lt = time.localtime() + timeStr = time.strftime(timeFmt, lt)[:maxCharsPerLine] + margin = 3 + spacing = getVertSpacing(4, margin) + timeX = getCenterStartPos(timeStr) + addString(timeStr, timeX, margin) + if counter % 100 == 0: + # We only perform the date/week checks/updates once every 100th + # iteration. We will maybe lag behind a couple of seconds when + # switching, but switching occurs seldom and it will be alot of + # unnecessary checks :). + dateStr = time.strftime(dateFmt, lt)[:maxCharsPerLine] + if recalcWeek: + week = calculateWeek(lt) + weekFmt = weekFmt.replace('%q', str(week)) + weekStr = time.strftime(weekFmt, lt)[:maxCharsPerLine] + dayStr = time.strftime(dayFmt, lt)[:maxCharsPerLine] + + dateX = getCenterStartPos(dateStr) + weekX = getCenterStartPos(weekStr) + dayX = getCenterStartPos(dayStr) + addString(dateStr, dateX, margin + spacing + letterWidth) + addString(dayStr, dayX, margin + 2 * (spacing + letterWidth)) + addString(weekStr, weekX, margin + 3 * (spacing + letterWidth)) + counter = 0 + pywmhelpers.redraw() + time.sleep(0.1) + +def parseColors(defaultRGBFileNames, config, xpm): + rgbFileName = '' + for fn in defaultRGBFileNames: + if os.access(fn, os.R_OK): + rgbFileName = fn + break + rgbFileName = config.get('rgbfile', rgbFileName) + useColors = 1 + if not os.access(rgbFileName, os.R_OK): + sys.stderr.write( + "Can't read the RGB file, try setting it differently using -r,\n") + sys.stderr.write( + "Ignoring your color settings, using the defaults.\n") + useColors = 0 + if useColors: + # Colors is a list with (, ) pairs. + colors = (('foreground', 'text'), + ('background', 'background')) + for key, value in colors: + col = config.get(key) + if not col is None: + code = pywmhelpers.getColorCode(col, rgbFileName) + if code is None: + sys.stderr.write('Bad colorcode for %s, ignoring.\n' % key) + else: + pywmhelpers.setColor(xpm, value, code) + +def main(): + clConfig = parseCommandLine(sys.argv) + configFile = clConfig.get('configfile', defaultConfigFile) + configFile = os.path.expanduser(configFile) + fileConfig = pywmhelpers.readConfigFile(configFile, sys.stderr) + # Merge the two configs, let the commandline options overwrite those in the + # configuration file. + config = fileConfig + for i in clConfig.iteritems(): + config[i[0]] = i[1] + + parseColors(defaultRGBFiles, config, xpm) + pywmhelpers.setDefaultPixmap(xpm) + timeFmt = config.get('timeformat', timeDefaultFormat) + dateFmt = config.get('dateformat', dateDefaultFormat) + dayFmt = config.get('weekdayformat', dayDefaultFormat) + weekFmt = config.get('weekformat', weekDefaultFormat) + # openXwindow sets the window title to the program name. If we get the + # program name with a path, split it so we only name the window with the + # filename. + try: + programName = sys.argv[0].split(os.sep)[-1] + except IndexError: # Should only happen when using the interpreter. + programName = '' + sys.argv[0] = programName + pywmhelpers.openXwindow(sys.argv, width, height) + mainLoop(timeFmt, dateFmt, dayFmt, weekFmt) + +xpm = \ +['160 100 11 1', + ' \tc #208120812081', + '.\tc #00000000FFFF', + 'o\tc #C71BC30BC71B', + 'O\tc #861782078E38', + '+\tc #EFBEF3CEEFBE', + '@\tc #618561856185', + '#\tc #9E79A2899E79', + '$\tc #410341034103', + '/\tc #2020b2b2aaaa s graph', + 'X\tc #000000000000 s background', + '%\tc #2081B2CAAEBA s text', + ' ................................................................................................', + ' ..///...........................................................................................', + ' ..///...........................................................................................', + ' ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///////////////////////////////////////////////////////////////////////////////////////////...', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ..///////////////////////////////////////////////////////////////////////////////////////////...', + ' ..///////////////////////////////////////////////////////////////////////////////////////////...', + ' ................................................................................................', + ' ................................................................................................', + ' ................................................................................................', + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%%%%%XXX%XXX%%%%%X%%%%%X%XXX%X%%%%%X%%%%%X%%%%%X%%%%%X%%%%%XXXXXXXXXX%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XX%%XXXXXXX%XXXXX%X%XXX%X%XXXXX%XXXXXXXXX%X%XXX%X%XXX%XX%%XXXXXX%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XXX%XXXXXXX%XXXXX%X%XXX%X%XXXXX%XXXXXXXXX%X%XXX%X%XXX%XX%%XXXXX%%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XXX%XXX%%%%%XX%%%%X%%%%%X%%%%%X%%%%%XXXXX%X%%%%%X%%%%%XXXXXXXXX%XXX%%%%%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XXX%XXX%XXXXXXXXX%XXXXX%XXXXX%X%XXX%XXXXX%X%XXX%XXXXX%XXXXXXXX%%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XXX%XXX%XXXXXXXXX%XXXXX%XXXXX%X%XXX%XXXXX%X%XXX%XXXXX%XX%%XXXX%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%%%%%XX%%%XX%%%%%X%%%%%XXXXX%X%%%%%X%%%%%XXXXX%X%%%%%X%%%%%XX%%XXXX%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + '................................................................................................................................................................', + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', + 'XX%%%XX%%%%XXX%%%%X%%%%XX%%%%XX%%%%%X%%%%%X%XXX%XXX%XXXXXXX%X%XXX%X%XXXXX%XXX%X%%%%XX%%%%%X%%%%%X%%%%%X%%%%%X%%%%%X%%%%%X%XXX%X%XXX%X%XXX%X%XXX%X%XXX%X%%%%%XXXX', + 'X%XXX%X%XXX%X%XXXXX%XXX%X%XXXXX%XXXXX%XXXXX%XXX%XXX%XXXXXXX%X%XXX%X%XXXXX%%X%%X%XXX%X%XXX%X%XXX%X%XXX%X%XXX%X%XXXXXXX%XXX%XXX%X%XXX%X%XXX%X%XXX%X%XXX%XXXXX%XXXX', + 'X%XXX%X%XXX%X%XXXXX%XXX%X%XXXXX%XXXXX%XXXXX%XXX%XXX%XXXXXXX%X%XX%XX%XXXXX%X%X%X%XXX%X%XXX%X%XXX%X%XXX%X%XXX%X%XXXXXXX%XXX%XXX%X%XXX%X%XXX%XX%X%XX%XXX%XXXX%XXXXX', + 'X%%%%%X%%%%XX%XXXXX%XXX%X%%%%XX%%%%XX%X%%%X%%%%%XXX%XXXXXXX%X%%%XXX%XXXXX%XXX%X%XXX%X%XXX%X%%%%%X%%XX%X%%%%XX%%%%%XXX%XXX%XXX%X%XXX%X%XXX%XXX%XXX%%%%%XXX%XXXXXX', + 'X%XXX%X%XXX%X%XXXXX%XXX%X%XXXXX%XXXXX%XXX%X%XXX%XXX%XXXXXXX%X%XX%XX%XXXXX%XXX%X%XXX%X%XXX%X%XXXXX%X%X%X%XXX%XXXXX%XXX%XXX%XXX%X%XXX%X%X%X%XX%X%XXXXXX%XX%XXXXXXX', + 'X%XXX%X%XXX%X%XXXXX%XXX%X%XXXXX%XXXXX%XXX%X%XXX%XXX%XXX%XXX%X%XXX%X%XXXXX%XXX%X%XXX%X%XXX%X%XXXXX%XX%%X%XXX%XXXXX%XXX%XXX%XXX%X%XXX%X%%X%%X%XXX%XXXXX%X%XXXXXXXX', + 'X%XXX%X%%%%XXX%%%%X%%%%XX%%%%XX%XXXXX%%%%%X%XXX%XXX%XXXX%%%XX%XXX%X%%%%XX%XXX%X%XXX%X%%%%%X%XXXXX%%%%%X%XXX%X%%%%%XXX%XXXX%%%%XX%%%XX%XXX%X%XXX%X%%%%%X%%%%%XXXX', + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................'] + +if __name__ == '__main__': + main() + diff --git a/pywmdatetime/sample.pywmdatetimerc b/pywmdatetime/sample.pywmdatetimerc new file mode 100644 index 0000000..98e55bc --- /dev/null +++ b/pywmdatetime/sample.pywmdatetimerc @@ -0,0 +1,42 @@ +[DEFAULT] +# The formats are the same as Python's strftime accept, but one format is +# added. %q now means the week-number with monday as the start of the week and +# the first day of the year is allways in week 1. We calculate like that for +# example here in Sweden. +# +# Taken from the Python manual: +# %a Locale's abbreviated weekday name. +# %A Locale's full weekday name. +# %b Locale's abbreviated month name. +# %B Locale's full month name. +# %c Locale's appropriate date and time representation. +# %d Day of the month as a decimal number [01,31]. +# %H Hour (24-hour clock) as a decimal number [00,23]. +# %I Hour (12-hour clock) as a decimal number [01,12]. +# %j Day of the year as a decimal number [001,366]. +# %m Month as a decimal number [01,12]. +# %M Minute as a decimal number [00,59]. +# %p Locale's equivalent of either AM or PM. +# %S Second as a decimal number [00,61]. +# %U Week number of the year (Sunday as the first day of the week) as a +# decimal number [00,53]. All days in a new year preceding the first +# Sunday are considered to be in week 0. +# %w Weekday as a decimal number [0(Sunday),6]. +# %W Week number of the year (Monday as the first day of the week) as a +# decimal number [00,53]. All days in a new year preceding the first +# Monday are considered to be in week 0. +# %x Locale's appropriate date representation. +# %X Locale's appropriate time representation. +# %y Year without century as a decimal number [00,99]. +# %Y Year with century as a decimal number. +# %Z Time zone name (or by no characters if no time zone exists). +# %% A literal "%" character. + +background=black +foreground=light sea green +timeformat=%H:%M:%S +dateformat=%d-%m-%y +weekdayformat=%A +weekformat=wk %q +#rgbfile=/usr/lib/X11/rgb.txt + diff --git a/pywmgeneral/INSTALL b/pywmgeneral/INSTALL new file mode 100644 index 0000000..0407361 --- /dev/null +++ b/pywmgeneral/INSTALL @@ -0,0 +1,10 @@ +1. Edit setup.py and make XLibDir point to the directory containing the + X shared libraries and make XIncludes point to the directory + containing your X includes. + +2. Su root. + +3. run './setup.py install' + +Now the library should be built and installed as an Python extension module. + diff --git a/pywmgeneral/PKG-INFO b/pywmgeneral/PKG-INFO new file mode 100644 index 0000000..1a96b84 --- /dev/null +++ b/pywmgeneral/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: pywmgeneral +Version: 0.1 +Summary: Python module for making WindowMaker dockapps. +Home-page: http://foo.unix.se/pywmdockapps +Author: Kristoffer Erlandsson +Author-email: krier115@student.liu.se +License: GPL +Description: UNKNOWN +Platform: UNKNOWN diff --git a/pywmgeneral/README b/pywmgeneral/README new file mode 100644 index 0000000..199b455 --- /dev/null +++ b/pywmgeneral/README @@ -0,0 +1,23 @@ +[WHAT] +Pywmgeneral is a python module that will help you develope WindowMaker +dockapps in python. It is mostly a wrapper around the functions from the +popular wmgeneral.c, but some new functions are added too. + +It also contains the Python written module pywmhelpers.py which contains +functions to aid the development of wm dockapps. This module contains python +functions that wrap up the functions which the extension module provides. +They ease up argument passing and give nicer return values. Some additional +functions, like help for handling a simple configuration file is also +available. This module is better documented than the pywmgeneral. It is +adviced to only use pywmhelpers and not touch the pywmgeneral module +directly at all. For information about how to use the module, see the +documentation in pywmhelpers.py. It is also possible to import it in the +interactive interpreter and issue 'help(pywmhelpers)'. + +[INSTALLATION] +See the file INSTALL + +[CONTACT] +Anything related to this piece of software can be e-mailed to me, Kristoffer +Erlandsson, . + diff --git a/pywmgeneral/pywmgeneral.c b/pywmgeneral/pywmgeneral.c new file mode 100644 index 0000000..58e2e2b --- /dev/null +++ b/pywmgeneral/pywmgeneral.c @@ -0,0 +1,596 @@ +/* pywmgeneral.c + * + * Python bindings to some of the most important functions in the widely used + * wmgeneral.c. Also some added functions. The rc file parser is removed since + * Python provide better facilities for this. + * + * Copyright (C) 2003 Kristoffer Erlandsson + * + * Licensed under the GNU General Public License. + * + * History: + * + * 2003-06-24 Kristoffer Erlandsson + * Added some additional event handling. + * + * 2003-06-16 Kristoffer Erlandsson + * Added checkForMouseClick to make catching of mouse clicks available from + * Python. + * + * 2003-06-14 Kristoffer Erlandsson + * Finished support for "everything" included in wmgeneral by default. + * + * 2003-06-13 Kristoffer Erlandsson + * File created made most of the pure wrapper functions and xpm inclusion. + * + * + * + * Thanks to Martijn Pieterse for createing the original wmgeneral.c + * +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "pywmgeneral.h" + + /*****************/ + /* X11 Variables */ +/*****************/ + +Window Root; +int screen; +int x_fd; +int d_depth; +XSizeHints mysizehints; +XWMHints mywmhints; +Pixel back_pix, fore_pix; +char *Geometry = ""; +Window iconwin, win; +GC NormalGC; +XpmIcon wmgen; +Pixmap pixmask; +Atom deleteAtom; /* Added 2003-06-24 for graceful shutdown. */ + +/*****************************************************************************/ +/* The Python stuff */ +/*****************************************************************************/ +static char **pixmap; /* Global pixmap, we only support one of these */ +static char *maskBits; /* Global maskbits, also only 1 supported */ + +char **pyListToStrs(PyObject *l) { + /* Convert a python list of strings to a char **. */ + int size, i; + char **target; + PyObject *s; + if (!PyList_Check(l)) { + PyErr_SetString(PyExc_TypeError, "List expected."); + return NULL; + } + size = PyList_Size(l); + target = (char **)malloc(size * sizeof(char *)); + for (i = 0; i < size; i++) { + s = PySequence_GetItem(l, i); + if (s == NULL) + return NULL; /* Shouldn't happen. */ + if (!PyString_Check(s)) { + PyErr_SetString(PyExc_TypeError, "String expected."); + return NULL; + } + target[i] = PyString_AsString(s); + } + return target; +} + + +static PyObject * +pywmgeneral_includePixmap(PyObject *self, PyObject *args) { + /* Set the global pixmap. */ + PyObject *arg; + if (!PyArg_ParseTuple(args, "O", &arg)) + return NULL; + if(!(pixmap = pyListToStrs(arg))) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +pywmgeneral_openXwindow(PyObject *self, PyObject *args) { + /* This function now uses the global variable pixmap as xpm and creates the + * xbm mask of the given height and width from this one. IOW no other xbm + * masks are supported at the moment. This shouldn't be needed except in + * special cases (I think...) + */ + int argc, width, height; + PyObject *argvTmp; + char **argv; + if (!PyArg_ParseTuple(args, "iOii", &argc, &argvTmp, &width, &height)) + return NULL; + if (!(argv = pyListToStrs(argvTmp))) + return NULL; + maskBits = (char *)malloc(width * height * sizeof(char)); + createXBMfromXPM(maskBits, pixmap, width, height); + openXwindow(argc, argv, pixmap, maskBits, width, height); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +pywmgeneral_redrawWindow(PyObject *self, PyObject *args) { + if (!PyArg_ParseTuple(args, "")) + return NULL; + RedrawWindow(); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +pywmgeneral_redrawWindowXY(PyObject *self, PyObject *args) { + int x, y; + if (!PyArg_ParseTuple(args, "ii", &x, &y)) + return NULL; + RedrawWindowXY(x, y); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +pywmgeneral_addMouseRegion(PyObject *self, PyObject *args) { + int index, left, top, right, bottom; + if (!PyArg_ParseTuple(args, "iiiii", &index, &left, &top, &right, &bottom)) + return NULL; + AddMouseRegion(index, left, top, right, bottom); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +pywmgeneral_checkMouseRegion(PyObject *self, PyObject *args) { + int x, y; + if (!PyArg_ParseTuple(args, "ii", &x, &y)) + return NULL; + return Py_BuildValue("i", CheckMouseRegion(x, y)); +} + +static PyObject * +pywmgeneral_copyXPMArea(PyObject *self, PyObject *args) { + /* x - source x, y - source y + * sx - width, sy - height + * dx - destination x, dy - destination y + * + * Variables named as in the original wmgeneral.c, don't blame me for it :) + */ + int x, y, sx, sy, dx, dy; + if (!PyArg_ParseTuple(args, "iiiiii", &x, &y, &sx, &sy, &dx, &dy)) + return NULL; + copyXPMArea(x, y, sx, sy, dx, dy); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +pywmgeneral_checkForEvents(PyObject *self, PyObject *args) { + /* If we find an event we handle, return a dicitionary containing some + * information about it. Return None if there are no events we handle. + * Ignore events we don't handle. Also we provide a handler for when the + * window is exposed, redraw it. + */ + XEvent event; + if (!PyArg_ParseTuple(args, "")) + return NULL; + while (XPending(display)) { + XNextEvent(display, &event); + if (event.type == Expose) { + RedrawWindow(); + } + else if (event.type == ClientMessage) { + if((Atom)event.xclient.data.l[0] == deleteAtom) { + XCloseDisplay(display); + return Py_BuildValue("{s:s}", "type", "destroynotify"); + } + } + else if (event.type == DestroyNotify) { + /* This seems to never happen, why? */ + XCloseDisplay(display); + return Py_BuildValue("{s:s}", "type", "destroynotify"); + } + else if (event.type == ButtonRelease) { + return Py_BuildValue("{s:s,s:i,s:i,s:i}", "type", "buttonrelease", + "button", event.xbutton.button, "x", + event.xbutton.x, "y", event.xbutton.y); + } + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef PyWmgeneralMethods[] = { + {"openXwindow", pywmgeneral_openXwindow, METH_VARARGS, + "Open the X window containing everything."}, + {"includePixmap", pywmgeneral_includePixmap, METH_VARARGS, + "Set the global pixmap that will be used as a mask and for everything else."}, + {"redrawWindow", pywmgeneral_redrawWindow, METH_VARARGS, + "Redraw the window."}, + {"redrawWindowXY", pywmgeneral_redrawWindowXY, METH_VARARGS, + "Redraw a give region of the window."}, + {"addMouseRegion", pywmgeneral_addMouseRegion, METH_VARARGS, + "Add a mouse region with a given index."}, + {"checkMouseRegion", pywmgeneral_checkMouseRegion, METH_VARARGS, + "Check if the given coordinates are in any mouse region."}, + {"copyXPMArea", pywmgeneral_copyXPMArea, METH_VARARGS, + "Copy an area of the global XPM."}, + {"checkForEvents", pywmgeneral_checkForEvents, METH_VARARGS, + "Check for some Xevents"}, + {NULL, NULL, 0, NULL} +}; + +void initpywmgeneral(void) { + Py_InitModule("pywmgeneral", PyWmgeneralMethods); +} + +/*****************************************************************************/ +/* Original C sources (With some modifications) */ +/*****************************************************************************/ + + /*****************/ + /* Mouse Regions */ +/*****************/ + +typedef struct { + int enable; + int top; + int bottom; + int left; + int right; +} MOUSE_REGION; + +MOUSE_REGION mouse_region[MAX_MOUSE_REGION]; + + /***********************/ + /* Function Prototypes */ +/***********************/ + +static void GetXPM(XpmIcon *, char **); +static Pixel GetColor(char *); +void RedrawWindow(void); +void AddMouseRegion(int, int, int, int, int); +int CheckMouseRegion(int, int); + +/*******************************************************************************\ +|* GetXPM *| +\*******************************************************************************/ + +static void GetXPM(XpmIcon *wmgen, char *pixmap_bytes[]) { + + XWindowAttributes attributes; + int err; + + /* For the colormap */ + XGetWindowAttributes(display, Root, &attributes); + + wmgen->attributes.valuemask |= (XpmReturnPixels | XpmReturnExtensions); + + err = XpmCreatePixmapFromData(display, Root, pixmap_bytes, &(wmgen->pixmap), + &(wmgen->mask), &(wmgen->attributes)); + + if (err != XpmSuccess) { + fprintf(stderr, "Not enough free colorcells.\n"); + exit(1); + } +} + +/*******************************************************************************\ +|* GetColor *| +\*******************************************************************************/ + +static Pixel GetColor(char *name) { + + XColor color; + XWindowAttributes attributes; + + XGetWindowAttributes(display, Root, &attributes); + + color.pixel = 0; + if (!XParseColor(display, attributes.colormap, name, &color)) { + fprintf(stderr, "wm.app: can't parse %s.\n", name); + } else if (!XAllocColor(display, attributes.colormap, &color)) { + fprintf(stderr, "wm.app: can't allocate %s.\n", name); + } + return color.pixel; +} + +/*******************************************************************************\ +|* flush_expose *| +\*******************************************************************************/ + +static int flush_expose(Window w) { + + XEvent dummy; + int i=0; + + while (XCheckTypedWindowEvent(display, w, Expose, &dummy)) + i++; + + return i; +} + +/*******************************************************************************\ +|* RedrawWindow *| +\*******************************************************************************/ + +void RedrawWindow(void) { + + flush_expose(iconwin); + XCopyArea(display, wmgen.pixmap, iconwin, NormalGC, + 0,0, wmgen.attributes.width, wmgen.attributes.height, 0,0); + flush_expose(win); + XCopyArea(display, wmgen.pixmap, win, NormalGC, + 0,0, wmgen.attributes.width, wmgen.attributes.height, 0,0); +} + +/*******************************************************************************\ +|* RedrawWindowXY *| +\*******************************************************************************/ + +void RedrawWindowXY(int x, int y) { + + flush_expose(iconwin); + XCopyArea(display, wmgen.pixmap, iconwin, NormalGC, + x,y, wmgen.attributes.width, wmgen.attributes.height, 0,0); + flush_expose(win); + XCopyArea(display, wmgen.pixmap, win, NormalGC, + x,y, wmgen.attributes.width, wmgen.attributes.height, 0,0); +} + +/*******************************************************************************\ +|* AddMouseRegion *| +\*******************************************************************************/ + +void AddMouseRegion(int index, int left, int top, int right, int bottom) { + + if (index < MAX_MOUSE_REGION) { + mouse_region[index].enable = 1; + mouse_region[index].top = top; + mouse_region[index].left = left; + mouse_region[index].bottom = bottom; + mouse_region[index].right = right; + } +} + +/*******************************************************************************\ +|* CheckMouseRegion *| +\*******************************************************************************/ + +int CheckMouseRegion(int x, int y) { + + int i; + int found; + + found = 0; + + for (i=0; i= mouse_region[i].left && + y <= mouse_region[i].bottom && + y >= mouse_region[i].top) + found = 1; + } + if (!found) return -1; + return (i-1); +} + +/*******************************************************************************\ +|* createXBMfromXPM *| +\*******************************************************************************/ +void createXBMfromXPM(char *xbm, char **xpm, int sx, int sy) { + + int i,j,k; + int width, height, numcol, depth; + int zero=0; + unsigned char bwrite; + int bcount; + int curpixel; + + sscanf(*xpm, "%d %d %d %d", &width, &height, &numcol, &depth); + + + for (k=0; k!=depth; k++) + { + zero <<=8; + zero |= xpm[1][k]; + } + + for (i=numcol+1; i < numcol+sy+1; i++) { + bcount = 0; + bwrite = 0; + for (j=0; j>= 1; + + curpixel=0; + for (k=0; k!=depth; k++) + { + curpixel <<=8; + curpixel |= xpm[i][j+k]; + } + + if ( curpixel != zero ) { + bwrite += 128; + } + bcount++; + if (bcount == 8) { + *xbm = bwrite; + xbm++; + bcount = 0; + bwrite = 0; + } + } + } +} + +/*******************************************************************************\ +|* copyXPMArea *| +\*******************************************************************************/ + +void copyXPMArea(int x, int y, int sx, int sy, int dx, int dy) { + + XCopyArea(display, wmgen.pixmap, wmgen.pixmap, NormalGC, x, y, sx, sy, dx, dy); + +} + +/*******************************************************************************\ +|* copyXBMArea *| +\*******************************************************************************/ + +void copyXBMArea(int x, int y, int sx, int sy, int dx, int dy) { + + XCopyArea(display, wmgen.mask, wmgen.pixmap, NormalGC, x, y, sx, sy, dx, dy); +} + + +/*******************************************************************************\ +|* setMaskXY *| +\*******************************************************************************/ + +void setMaskXY(int x, int y) { + + XShapeCombineMask(display, win, ShapeBounding, x, y, pixmask, ShapeSet); + XShapeCombineMask(display, iconwin, ShapeBounding, x, y, pixmask, ShapeSet); +} + +/*******************************************************************************\ +|* openXwindow *| +\*******************************************************************************/ +void openXwindow(int argc, char *argv[], char *pixmap_bytes[], char *pixmask_bits, int pixmask_width, int pixmask_height) { + + unsigned int borderwidth = 1; + XClassHint classHint; + char *display_name = NULL; + char *wname = argv[0]; + XTextProperty name; + + XGCValues gcv; + unsigned long gcm; + + char *geometry = NULL; + + int dummy=0; + int i, wx, wy; + + /* Changed to work better with Python. Changed check in for loop to control + * argc instead of argv. + */ + for (i=1; i < argc; i++) { + if (!strcmp(argv[i], "-display")) { + display_name = argv[i+1]; + i++; + } + if (!strcmp(argv[i], "-geometry")) { + geometry = argv[i+1]; + i++; + } + } + + if (!(display = XOpenDisplay(display_name))) { + fprintf(stderr, "%s: can't open display %s\n", + wname, XDisplayName(display_name)); + exit(1); + } + screen = DefaultScreen(display); + Root = RootWindow(display, screen); + d_depth = DefaultDepth(display, screen); + x_fd = XConnectionNumber(display); + + /* Convert XPM to XImage */ + GetXPM(&wmgen, pixmap_bytes); + + /* Create a window to hold the stuff */ + mysizehints.flags = USSize | USPosition; + mysizehints.x = 0; + mysizehints.y = 0; + + back_pix = GetColor("white"); + fore_pix = GetColor("black"); + + XWMGeometry(display, screen, Geometry, NULL, borderwidth, &mysizehints, + &mysizehints.x, &mysizehints.y,&mysizehints.width,&mysizehints.height, &dummy); + + mysizehints.width = 64; + mysizehints.height = 64; + + win = XCreateSimpleWindow(display, Root, mysizehints.x, mysizehints.y, + mysizehints.width, mysizehints.height, borderwidth, fore_pix, back_pix); + + iconwin = XCreateSimpleWindow(display, win, mysizehints.x, mysizehints.y, + mysizehints.width, mysizehints.height, borderwidth, fore_pix, back_pix); + + + /* Added 2003-06-24 for graceful shutdown. */ + deleteAtom = XInternAtom(display, "WM_DELETE_WINDOW", 0); + XSetWMProtocols(display, win, &deleteAtom, 1); + + + /* Activate hints */ + XSetWMNormalHints(display, win, &mysizehints); + classHint.res_name = wname; + classHint.res_class = wname; + XSetClassHint(display, win, &classHint); + + XSelectInput(display, win, ButtonPressMask | ExposureMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask); + XSelectInput(display, iconwin, ButtonPressMask | ExposureMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask); + + if (XStringListToTextProperty(&wname, 1, &name) == 0) { + fprintf(stderr, "%s: can't allocate window name\n", wname); + exit(1); + } + + XSetWMName(display, win, &name); + + /* Create GC for drawing */ + + gcm = GCForeground | GCBackground | GCGraphicsExposures; + gcv.foreground = fore_pix; + gcv.background = back_pix; + gcv.graphics_exposures = 0; + NormalGC = XCreateGC(display, Root, gcm, &gcv); + + /* ONLYSHAPE ON */ + + pixmask = XCreateBitmapFromData(display, win, pixmask_bits, pixmask_width, pixmask_height); + + XShapeCombineMask(display, win, ShapeBounding, 0, 0, pixmask, ShapeSet); + XShapeCombineMask(display, iconwin, ShapeBounding, 0, 0, pixmask, ShapeSet); + + /* ONLYSHAPE OFF */ + + mywmhints.initial_state = WithdrawnState; + mywmhints.icon_window = iconwin; + mywmhints.icon_x = mysizehints.x; + mywmhints.icon_y = mysizehints.y; + mywmhints.window_group = win; + mywmhints.flags = StateHint | IconWindowHint | IconPositionHint | WindowGroupHint; + + XSetWMHints(display, win, &mywmhints); + + XSetCommand(display, win, argv, argc); + XMapWindow(display, win); + + if (geometry) { + if (sscanf(geometry, "+%d+%d", &wx, &wy) != 2) { + fprintf(stderr, "Bad geometry string.\n"); + exit(1); + } + XMoveWindow(display, win, wx, wy); + } +} diff --git a/pywmgeneral/pywmgeneral.h b/pywmgeneral/pywmgeneral.h new file mode 100644 index 0000000..f532437 --- /dev/null +++ b/pywmgeneral/pywmgeneral.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2003 Kristoffer Erlandsson + * + * Licensed under the GNU General Public License. + * Copyright (C) 2003 Kristoffer Erlandsson + * + * Licensed under the GNU General Public License. + */ + +#ifndef WMGENERAL_H_INCLUDED +#define WMGENERAL_H_INCLUDED + + /***********/ + /* Defines */ +/***********/ + +#define MAX_MOUSE_REGION (16) + + /************/ + /* Typedefs */ +/************/ + +typedef struct _rckeys rckeys; + +struct _rckeys { + const char *label; + char **var; +}; + +typedef struct _rckeys2 rckeys2; + +struct _rckeys2 { + const char *family; + const char *label; + char **var; +}; + +typedef struct { + Pixmap pixmap; + Pixmap mask; + XpmAttributes attributes; +} XpmIcon; + + /*******************/ + /* Global variable */ +/*******************/ + +Display *display; + + /***********************/ + /* Function Prototypes */ +/***********************/ + +void AddMouseRegion(int index, int left, int top, int right, int bottom); +int CheckMouseRegion(int x, int y); + +void openXwindow(int argc, char *argv[], char **, char *, int, int); +void RedrawWindow(void); +void RedrawWindowXY(int x, int y); + +void createXBMfromXPM(char *, char **, int, int); +void copyXPMArea(int, int, int, int, int, int); +void copyXBMArea(int, int, int, int, int, int); +void setMaskXY(int, int); + +#endif diff --git a/pywmgeneral/pywmhelpers.py b/pywmgeneral/pywmhelpers.py new file mode 100644 index 0000000..bda6ad3 --- /dev/null +++ b/pywmgeneral/pywmhelpers.py @@ -0,0 +1,253 @@ +"""pywmhelpers.py + +Various helper functions when writing wm dockapps in Python. This module +is way better commented than the pywmgeneral one. This is the one +intented for use in applications. Many functions are just wrappers +around the ones in pywmgeneral but with nicer interfaces and better +documentation. + +Copyright (C) 2003 Kristoffer Erlandsson + +Licensed under the GNU General Public License + + +Changes: +2003-06-25 Kristoffer Erlandsson +Updated documentation + +2003-06-24 Kristoffer Erlandsson +Some changes to handle the additional event handling in pywmgeneral + +2003-06-16 Kristoffer Erlandsson +First workingish version +""" + +import os +import re +import ConfigParser + +import pywmgeneral + +def readConfigFile(fileName, errOut): + """Read the config file fileName. + + Return a dictionary with the options and values in the DEFAULT + section. Ignore everything else. The configuration file should not + get so complicated so that sections are needed. errOut is the + file-like object to which error messages will be printed. + """ + if not os.access(fileName, os.R_OK): + if errOut: + errOut.write( + 'Configuration file is not readable. Using defaults.\n') + return {} + cp = ConfigParser.ConfigParser() + try: + cp.read(fileName) + except ConfigParser.Error, e: + if errOut: + errOut.write('Error in configuration file:\n') + errOut.write(str(e) + '\nUsing defaults.') + return {} + defaults = cp.defaults() + if defaults == {}: + if errOut: + errOut.write( + 'Missing or empty DEFAULT section in the config file.\n') + errOut.write('Using defaults.\n') + return defaults + +def getCenterStartPos(s, letterWidth, areaWidth, offset): + """Get the x starting position if we want to paint s centred.""" + w = len(s) * letterWidth + textArea = areaWidth - offset * 2 - 1 + return (textArea - w) / 2 + +def addChar(ch, x, y, letterWidth, letterHeight, lettersStartX, lettersStartY, + letters, digitWidth, digitHeight, digitsStartX, digitsStartY, + digits, xOffset, yOffset, width, height): + """Paint the character ch at position x, y in the window. + + Return the (width, height) of the character painted. Raise + ValueError if we try to paint a char not in letters or digits + or if we get out of bounds during painting. digits is really + just another line of chars, it's unlucky called digits everywhere + I used it since it contained only digits in the start. However, + now it contains various special chars too everywhere I use it. But + the name remains in too many places so I haven't gotten to change + it. + """ + chIndex = letters.find(ch.lower()) + if chIndex != -1: + chX = lettersStartX + chIndex * letterWidth + chY = lettersStartY + w = letterWidth + h = letterHeight + else: + chIndex = digits.find(ch) + if chIndex != -1: + chX = digitsStartX + chIndex * digitWidth + chY = digitsStartY + w = digitWidth + h = digitHeight + else: + raise ValueError, "Unsupported char: '%s'" % ch + targX = x + xOffset + targY = y + yOffset + if targX + w > width - xOffset or targY + h > height - yOffset\ + or targX < 0 or targY < 0: + raise ValueError, "Out of bounds." + pywmgeneral.copyXPMArea(chX, chY, w, h, targX, targY) + return (w, h) + +def addString(s, x, y, letterWidth, letterHeight, lettersStartX, lettersStartY, + letters, digitWidth, digitHeight, digitsStartX, digitsStartY, + digits, xOffset, yOffset, width, height): + """Add a string at the given x and y positions. + + Call addChar repeatedely, so the same exception rules apply.""" + lastW = 0 + for letter in s: + w, h = addChar(letter, x + lastW, y, letterWidth, letterHeight, + lettersStartX, lettersStartY, letters, digitWidth, + digitHeight, digitsStartX, digitsStartY, digits, + xOffset, yOffset, width, height) + lastW += w + +def getVertSpacing(numLines, margin, height, letterHeight, yOffset): + """Return the optimal spacing between a number of lines. + + margin is the space we want between the first line and the top.""" + h = height - numLines * letterHeight - yOffset * 2 - margin + return h / (numLines - 1) + + +def readXPM(fileName): + """Read the xpm in filename. + + Return a list of strings containing the xpm. Raise IOError if we run + into trouble when trying to read the file. This function surely + doesn't handle all XPMs, but it handles the ones I use, so that'll + do. + """ + f = file(fileName, 'r') + lines = [l.rstrip('\n') for l in f.readlines()] + s = ''.join(lines) + res = [] + while 1: + nextStrStart = s.find('"') + if nextStrStart != -1: + nextStrEnd = s.find('"', nextStrStart + 1) + if nextStrEnd != -1: + res.append(s[nextStrStart+1:nextStrEnd]) + s = s[nextStrEnd+1:] + continue + break + return res + +def setColor(xpm, name, newColor): + """Find the color with comment name and set this color to newColor. + + Change the source code of an XPM represented as a list of strings. + I'm certain that this will fail on many XPMs too, but it'll do for + the ones I use. No check is done that the color is valid, this has + to be done elsewhere. + """ + colorRE = re.compile( + r"^(?P.).*?c (?P#.*?) s (?P.*)") + index = 1 + for line in xpm[1:]: + m = colorRE.match(line) + if not m is None: + comment = m.group('comment') + if comment == name: + letter = m.group('letter') + color = newColor + xpm[index] = '%s\tc %s s %s' % (letter, color, comment) + index += 1 + +def setDefaultPixmap(xpm): + """Set the pixmap of the program. + + xpm is an XPM represented as a list of strings, possible gotten + from readXPM(). This is what we use all the time later. The XBM + mask is created out of the XPM. If I understand correctly how it + works we use the upper left rectangle as mask. This is the + simplest way to do it and is the desired behaviour in most cases. + """ + pywmgeneral.includePixmap(xpm) + +def openXwindow(argv, w, h): + """Open the X window of given width and height. + + The XBM mask is here created from the upper left rectangle of the + XPM using the given width and height.""" + pywmgeneral.openXwindow(len(argv), argv, w, h) + +def redraw(): + """Redraw the window.""" + pywmgeneral.redrawWindow() + +def redrawXY(x, y): + """Redraw a given region of the window.""" + pywmgeneral.redrawWindowXY(x, y) + +def copyXPMArea(sourceX, sourceY, width, height, targetX, targetY): + """Copy an area of the global XPM.""" + if width > 0 or height > 0: + pywmgeneral.copyXPMArea(sourceX, sourceY, width, height, + targetX, targetY) + +def addMouseRegion(index, left, top, right, bottom): + """Add a mouse region in the window.""" + pywmgeneral.addMouseRegion(index, left, top, right, bottom) + +def checkMouseRegion(x, y): + """Check if x,y is in any mouse region. Return that region, otherwise -1. + """ + return pywmgeneral.checkMouseRegion(x, y) + +def getEvent(): + """Check for XEvents and return one if found. + + Return None if we find no events. There may be events pending still + after this function is called. If an event which we handle is found, + return a dictionary with information about it. All dictionaries + contain a 'type' field identifying the event. Now existing events + with dictionary keys are: + 'buttonrelease': + x, y, button + 'destroynotify': + """ + return pywmgeneral.checkForEvents() + +def getColorCode(colorName, rgbFileName): + """Convert a color to rgb code usable in an xpm. + + We use the file rgbFileName for looking up the colors. Return None + if we find no match. The rgbFileName should be like the one found in + /usr/lib/X11R6/rgb.txt on most sytems. + """ + f = file(rgbFileName, 'r') + lines = f.readlines() + f.close() + for l in lines: + if l[0] != '!': + words = l.split() + if len(words) > 3: + name = ' '.join(words[3:]) + if colorName.lower() == name.lower(): + # Found the right color, get it's code + try: + r = int(words[0]) + g = int(words[1]) + b = int(words[2]) + except ValueError: + continue + rgbstr = '#' + str(hex(r))[2:].zfill(2) + \ + str(hex(g))[2:].zfill(2) + \ + str(hex(b))[2:].zfill(2) + return rgbstr + return None + + diff --git a/pywmgeneral/setup.py b/pywmgeneral/setup.py new file mode 100755 index 0000000..9e66dad --- /dev/null +++ b/pywmgeneral/setup.py @@ -0,0 +1,26 @@ +#! /usr/bin/env python + +from distutils.core import setup, Extension + + +# Set these so they match your system. +XLibDir = '/usr/X11R6/lib' +XIncludes = '/usr/X11R6/include' + + +module1 = Extension('pywmgeneral', + libraries = ['Xpm', 'Xext', 'X11'], + library_dirs = [XLibDir], + include_dirs = [XIncludes], + sources = ['pywmgeneral.c']) + +setup(name = 'pywmgeneral', + py_modules = ['pywmhelpers'], + version = '0.1', + author = 'Kristoffer Erlandsson', + author_email = 'krier115@student.liu.se', + description = 'Python module for making WindowMaker dockapps.', + url = 'http://foo.unix.se/pywmdockapps', + license = 'GPL', + ext_modules = [module1]) + diff --git a/pywmgeneric/ChangeLog b/pywmgeneric/ChangeLog new file mode 100644 index 0000000..0f2dc05 --- /dev/null +++ b/pywmgeneric/ChangeLog @@ -0,0 +1,8 @@ +2003-07-02 Kristoffer Erlandsson + * Added support for up to 10 mouse buttons. + * The char translation now handles both upper and lower case. + +2003-06-29 Kristoffer Erlandsson + * Additional error checking around string interpolations from cfg file. + + diff --git a/pywmgeneric/INSTALL b/pywmgeneric/INSTALL new file mode 100644 index 0000000..96e5101 --- /dev/null +++ b/pywmgeneric/INSTALL @@ -0,0 +1,13 @@ +1. If you don't have pywmgeneral installed, install it first. Can be found + at http://foo.unix.se/pywmdockapps. + +2. Copy pywmgeneric.py to somewhere in your path (like ~/bin or + /usr/local/bin). + +3. Create your own pywmgenericrc, save it as ~/.pywmgenericrc. Use the + sample.pywmgenericrc as reference. + +4. Enjoy. + +Trouble? E-mail me at . + diff --git a/pywmgeneric/README b/pywmgeneric/README new file mode 100644 index 0000000..717bab2 --- /dev/null +++ b/pywmgeneric/README @@ -0,0 +1,69 @@ +[WHAT] +Pywmgeneric is a dockapp with five entrys that display the first line of +output from an external program, the returned string from an python +method or an static string. Three mouse actions can be associated with +each displayed entry. + +[DETAILED] +Five different entries can be defined in pywmgeneric. Every entry can +have an action, an update_delay and up to three mouse actions associated +with it. Some additional options are also available. + +The associated action of an entry is executed with update_delay time +between executions. The output from the action is stored. If no special +display options are defined, the application will display the first line +of output from the action. If it does not fit in the window, it will +slowly scroll in the window. Clicking with the mouse on the text invokes +one of the mouse actions, depending on which button was pressed. The +action can be to execute an external program, to run a python method or +to update the text through performing the action associated with the +entry. The mouse actions can retreive the text genererated by the timed +action. + +Python methods that should be executed as actions should be defined in +the class UserMethods. Look in pywmgeneric.py, near the top, for this +class and the documentation of how these methods should be defined. +Note that the methods allready there only are samples and will probably +not work on your system. + +Other options in the configuration file include: +scroll = yes|no - en-/disable scrolling of the text when it doesn't fit +display = - display a static string instead of the first line of + the action-generated output. + +See the sample configuration file for examples and more information. +Note that this file is only for reference, it is the one I use. Things +will probably not work on your system if you do not change it. + +[USES] +This program is very generic (hence the name ;) ), the uses are many +since it is highly configurable. + +I use it for displaying my cpu and system temperatures. I just defined +methods for reading two files in the /proc filesystem on my system. + +I also use it for fetching headlines from a newspaper, displaying the +first headline fetched. If I click with my left button, all headlines +will appear in an xmessage. If I rightclick the headlines along with +summaries are displayed, and if I click with my middle button mozilla +will fire up showing the newspaper's website. + +I have an external program which displays what's currently on tv, +ideal for use with this program I thought! I modified it a bit so +it printed a summary line at the top, and voila I have all +currently running tv programs scrolling by in an dockapp. And clicking +on it shows me the details. + +You could use it as an application launcher, just display the name of +the applications and associate mouse actions to lauch them. The +xterm-entry in the sample shows this. + +You could probably come up with much more than this! + +[CONTACT] +Mail bug reports, suggestions, comments, complaints or anything else, +be it funny or boring, related to this program to me, Kristoffer +Erlandsson, . + +The newest version can be found at http://foo.unix.se/pywmdockapps/. + diff --git a/pywmgeneric/pywmgeneric.py b/pywmgeneric/pywmgeneric.py new file mode 100755 index 0000000..bed9cf3 --- /dev/null +++ b/pywmgeneric/pywmgeneric.py @@ -0,0 +1,710 @@ +#!/usr/bin/env python + +'''pywmgeneric.py + +WindowMaker dockapp to display the output from an external program, or the +string returned from a python function. Mouse actions can be associated with +the displayed text. + +Copyright (C) 2003 Kristoffer Erlandsson + +Licensed under the GNU General Public License. + + +Changes +2003-07-02 Kristoffer Erlandsson +Added support for up to 10 mouse buttons. +The char translation now handles both upper and lower case. + +2003-06-29 Kristoffer Erlandsson +Additional error checking around string interpolations from cfg file. + +2003-06-27 Kristoffer Erlandsson +First working version +''' + +usage = '''pywmgeneric.py [options] +Available options are: + -h, --help print this help + -t, --text set the text color + -b, --background set the background color + -r, --rgbfile set the rgb file to get color codes from + -c, --configfile set the config file to use +''' + +import sys +import os +import time +import string +import ConfigParser +import getopt +import popen2 + +import pywmhelpers + +class UserMethods: + '''Put methods that should be called when the action is method=... here. + + The action methods should return a function, which in turn returns + the string to be displayed (if no 'display =' exists) and stored + for later retreival. + + The mouse action methods gets the entry instance as an argument. Return + value doesn't matter. + + An instance of this class is created at initialization and passed to all + entries, so keep in mind that they share the same object. + + THE METHODS ALLREADY HERE ARE JUST SAMPLES AND WILL PROBABLY NOT WORK + WITH YOUR SYSTEM. + ''' + def getCpuTemp(self): + try: + f = file('/proc/sys/dev/sensors/w83697hf-isa-0290/temp2', 'r') + except IOError: + return lambda: 'error' + temp = f.readline().split()[2] + f.close() + return lambda: 'cpu: %s' % temp + + def getSysTemp(self): + try: + f = file('/proc/sys/dev/sensors/w83697hf-isa-0290/temp1', 'r') + except IOError: + return lambda: 'error' + temp = f.readline().split()[2] + f.close() + return lambda: 'sys: %s' % temp + + def showDnWithoutDescs(self, entry): + '''Strip descriptions from some text where the descs are indented. + + Display it in an xmessage. + ''' + text = entry.getAllText() + s = '\n'.join([x for x in text.split('\n') if not x.startswith(' ')]) + os.system('xmessage "' + s.replace('"', r'\"') + '" &') + + def showTvWithoutDescs(self, entry): + '''Strip descriptions from some text where the descs are indented. + + Display it in an xmessage. + ''' + text = entry.getAllText() + s='\n'.join([x for x in + text.split('\n')[1:] if not x.startswith(' ')]) + s = s.replace('\n\n', '\n') + os.system('xmessage "' + s.replace('"', r'\"') + '" &') + +width = 64 +height = 64 + +xOffset = 4 +yOffset = 4 + +lettersStartX = 0 +lettersStartY = 74 +letterWidth = 6 +letterHeight = 8 + +digitsStartX = 0 +digitsStartY = 64 +digitWidth = 6 +digitHeight = 8 + +letters = 'abcdefghijklmnopqrstuvwxyz' +digits = '0123456789:/-%. ' + +maxChars = 9 + +defaultConfigFile = '~/.pywmgenericrc' +defaultRGBFiles = ('/usr/lib/X11/rgb.txt', '/usr/X11R6/lib/X11/rgb.txt') + +err = sys.stderr.write + +def addString(s, x, y): + '''Convenience function around pwymhelpers.addString.''' + try: + pywmhelpers.addString(s, x, y, letterWidth, letterHeight, lettersStartX, + lettersStartY, letters, digitWidth, digitHeight, + digitsStartX, digitsStartY, digits, xOffset, yOffset, + width, height) + except ValueError, e: + sys.stderr.write('Error when painting string:\n' + str(e) + '\n') + sys.exit(3) + +def clearLine(y): + '''Clear a line of text at position y.''' + pywmhelpers.copyXPMArea(72, yOffset, width - 2 * xOffset, letterHeight, + xOffset, y + yOffset) + +def getXY(line): + '''Return the x and y positions to be used at line line.''' + return 0, line * (letterHeight + 3) + 1 + +def isTrue(s): + '''Return true if the string s can be interpreted as a true value. + + Raises ValueError if we get a string we don't like. + ''' + trueThings = ['on', 'yes', '1', 'true'] + falseThings = ['off', 'no', '0', 'false'] + if s in trueThings: + return 1 + elif s in falseThings: + return 0 + raise ValueError + + +class Entry: + def __init__(self, line, updateDelay, action, mouseActions, + userMethods, display=None, scrollText=1): + self._updateDelay = updateDelay + self._line = line + self._action = self._parseAction(action) + self._mouseActions = [self._parseAction(a) for a in mouseActions] + self._userMethods = userMethods + self._display = display + self._scrollText = scrollText + + self._scrollPos = 0 + self._tickCount = 0L + + self._runningProcs = [] + self._actionProc = None + self._getTextMethod = None + self._allText = '' + self._displayLine = '' + # Do one action when we start, so we are sure that one gets done even + # if we do not want any other updates. + self._doAction() + self._lastActionAt = time.time() + + def _parseAction(self, action): + '''Parse an action string, return (, ). + + Or none if we get an empty action.''' + if action: + whatToDo = action.split()[0] + argStr = action[len(whatToDo):].lstrip() + return (whatToDo, argStr) + return None + + def _execExternal(self, command): + '''Exec an external command in the background. + + Return the running process as created by Popen3().''' + proc = popen2.Popen3(command) + self._runningProcs.append(proc) + return proc + + def _doMouseAction(self, button): + '''Perform the mouse action associated with a button.''' + if len(self._mouseActions) < button: + return # Just for safety, shouldn't happen. + item = self._mouseActions[button - 1] + if item: + # We have an action associated with the button. + action, arg = item + else: + # No action associated with the button. + return + if action == 'exec': + self._execExternal(self._expandStr(arg)) + elif action == 'method': + try: + method = getattr(self._userMethods, arg) + except AttributeError: + method = None + if method: + method(self) + else: + err("Warning: Method %s does not exist." % arg) + elif action == 'update': + self._doAction() + else: + err("Warning: Unknown mouse action: %s, ignoring.\n" % action) + + def _doAction(self): + '''Perform the action associated with this entry.''' + if self._action is None: + return + action, arg = self._action + if action == 'exec': + if self._actionProc is None : + self._actionProc = self._execExternal(arg) + else: + if not self._actionProc in self._runningProcs: + # The action process since the last time is finished, we + # can start another one without risking that we get + # flooded by processes. + self._actionProc = self._execExternal(arg) + self._getTextMethod = self._readFromActionProc + elif action == 'method': + try: + method = getattr(self._userMethods, arg) + except AttributeError: + method = None + if method: + self._getTextMethod = method() + else: + err('Warning: method %s does not exist. Ignoring.\n' % arg) + else: + err("Warning: Unknown action: %s, ignoring.\n" % action) + + def _readFromActionProc(self): + '''If our action process is ready, return the output. Otherwise None. + ''' + if self._actionProc.poll() == -1: + # Wait until the process is ready before we really read the text. + return None + # fromchild.read() will return '' if we allready have read the output + # so there will be no harm in calling this method more times. + return self._actionProc.fromchild.read() + + def _reapZombies(self): + '''Poll all running childs. This will reap all zombies.''' + i = 0 + for p in self._runningProcs: + val = p.poll() + if val != -1: + self._runningProcs.pop(i) + i += 1 + + def _updateText(self): + '''Get the text, update the display if it has changed. + ''' + text = '' + if self._getTextMethod: + text = self._getTextMethod() + # Only change the text if we get anything from the getTextMethod() + if text: + self._allText = text + if self._display is None: + # We have no display = in the config file, we want to + # display the first line of the output of the action. + if text: + displayLine = text.split(os.linesep)[0] + else: + displayLine = self._displayLine + else: + displayLine = self._display + if displayLine != self._displayLine: + # Line to display has changed, display the new one. + self._displayLine = displayLine + self._scrollPos = 0 + self.displayText(displayLine) + elif len(self._displayLine) > maxChars and self._scrollText: + # Line is the same and is longer than the display and we + # want to scroll it. + if self._tickCount % 2 == 0: + # Only scroll every third tick. + self._scrollAndDisplay() + + def _scrollAndDisplay(self): + '''Scroll the text one step to the left and redisplay it. + + When reaching the end, paint number of spaces before scrolling in the + same line again from the right. + ''' + if self._scrollPos >= \ + len(self._displayLine) + (maxChars - 4): + self._scrollPos = 0 + self.displayText(self._displayLine) + elif self._scrollPos >= len(self._displayLine) - 3: + self._scrollPos += 1 + disp = self._displayLine[self._scrollPos:] + \ + ' ' * (maxChars - 3) + diff = self._scrollPos - len(self._displayLine) + if diff > 0: + disp = disp[diff:] + disp += self._displayLine + self.displayText(disp) + else: + self._scrollPos += 1 + self.displayText( + self._displayLine[self._scrollPos:]) + + def tick1(self): + '''Do things that should be done often. + ''' + self._tickCount += 1 + self._reapZombies() + self._updateText() + currTime = time.time() + if not self._updateDelay is None and \ + currTime - self._lastActionAt > self._updateDelay: + # We want to do this last in the tick so the command gets the time + # to finish before the next tick (if it's a fast one). + self._lastActionAt = currTime + self._doAction() + + def tick2(self): + '''Do things that should be done a bit less often. + ''' + pass + + def translateText(self, text): + '''Translate chars that can't be painted in the app to something nicer. + + Or nothing if we can't come up with something good. Could be nice to + extend this function with chars more fitting for your language. + ''' + fromChars = 'åäöèü' + toChars = 'aaoeu' + deleteChars = [] + for c in text.lower(): + if not (c in letters or c in digits or c in fromChars): + deleteChars.append(c) + deleteChars = ''.join(deleteChars) + trans = string.maketrans(fromChars, toChars) + text = string.translate(text.lower(), trans, deleteChars) + return text + + def getAllText(self): + return self._allText + + def getDisplayedLine(self): + return self._displayLine + + def _expandStr(self, s): + '''Expand s, which now should be a line from an on_mouseX field. + ''' + try: + res = s % {'allText' : self._allText, + 'displayedLine' : self._displayLine, + 'allTextEscaped' : self._allText.replace('"', r'\"'), + 'allTextButFirstLine' : + '\n'.join(self._allText.split('\n')[1:]), + 'allTextButFirstLineEscaped' : + '\n'.join(self._allText.replace('"', '\"'). + split('\n')[1:])} + except (KeyError, TypeError, ValueError): + err( + "Warning: %s doesn't expand correctly. Ignoring interpolations.\n" + % s) + res = s + return res + + def displayText(self, text): + '''Display text on the entry's line. + + Remove or translate characters that aren't supported. Truncate the text + to fit in the app. + ''' + x, y = getXY(self._line) + clearLine(y) + addString(self.translateText(text)[:maxChars], x, y) + + def mouseClicked(self, button): + '''A mouse button has been clicked, do things.''' + if 0 < button < 11: + self._doMouseAction(button) + +class PywmGeneric: + def __init__(self, config): + self._entrys = [] + line = 0 + um = UserMethods() + for c in config: + # Create our 5 entrys. + if not c: + self._entrys.append(None) + line += 1 + continue + delay = c.get('update_delay') + if not delay is None: + try: + delay = self.parseTimeStr(delay) + except ValueError: + err("Malformed update_delay in section %s. " + % str(i)) + err("Ignoring this section.\n") + self._entrys.append(None) + line += 1 + continue + action = c.get('action') + display = c.get('display') + if action is None and display is None: + err( + "Warning: No action or display in section %d, ignoring it.\n" + % i) + self._entrys.append(None) + else: + scroll = isTrue(c.get('scroll', '1')) + # Get the mouse actions. + mouseActions = [] + for i in range(10): + but = str(i + 1) + opt = 'on_mouse' + but + mouseActions.append(c.get(opt)) + self._entrys.append(Entry(line, delay, action, + mouseActions, um, display, scroll)) + line += 1 + self._setupMouseRegions() + + def _setupMouseRegions(self): + for i in range(5): + x, y = getXY(i) + if not self._entrys[i] is None: + pywmhelpers.addMouseRegion(i, x + xOffset, y + yOffset, + width - 2 * xOffset, y + yOffset + letterHeight) + + def parseTimeStr(self, timeStr): + '''Take a string on a form like 10h and return the number of seconds. + + Raise ValueError if timeStr is on a bad format. + ''' + multipliers = {'s' : 1, 'm' : 60, 'h' : 3600} + timeStr = timeStr.strip() + if timeStr: + timeLetter = timeStr[-1] + multiplier = multipliers.get(timeLetter) + if not multiplier is None: + timeNum = float(timeStr[:-1].strip()) + numSecs = timeNum * multiplier + return numSecs + raise ValueError, 'Invalid literal' + + def _checkForEvents(self): + event = pywmhelpers.getEvent() + while not event is None: + if event['type'] == 'destroynotify': + sys.exit(0) + elif event['type'] == 'buttonrelease': + region = pywmhelpers.checkMouseRegion(event['x'], event['y']) + button = event['button'] + if region != -1: + if not self._entrys[region] is None: + self._entrys[region].mouseClicked(button) + event = pywmhelpers.getEvent() + + def mainLoop(self): + counter = -1 + while 1: + counter += 1 + self._checkForEvents() + if counter % 3 == 0: + [e.tick1() for e in self._entrys if not e is None] + if counter % 100 == 0: + [e.tick2() for e in self._entrys if not e is None] + + if counter == 999999: + counter = -1 + pywmhelpers.redraw() + time.sleep(0.1) + +def parseCommandLine(argv): + '''Parse the commandline. Return a dictionary with options and values.''' + shorts = 'ht:b:r:c:' + longs = ['help', 'text=', 'background=', 'rgbfile=', 'configfile='] + try: + opts, nonOptArgs = getopt.getopt(argv[1:], shorts, longs) + except getopt.GetoptError, e: + err('Error when parsing commandline: ' + str(e) + '\n') + err(usage) + sys.exit(2) + d = {} + for o, a in opts: + if o in ('-h', '--help'): + sys.stdout.write(usage) + sys.exit(0) + if o in ('-t', '--text'): + d['textcolor'] = a + if o in ('-b', '--background'): + d['background'] = a + if o in ('-r', '--rgbfile'): + d['rgbfile'] = a + if o in ('-c', '--configfile'): + d['configfile'] = a + return d + +def parseColors(defaultRGBFileList, config, xpm): + rgbFileName = '' + for fn in defaultRGBFileList: + if os.access(fn, os.R_OK): + rgbFileName = fn + break + rgbFileName = config.get('rgbfile', rgbFileName) + useColors = 1 + if not os.access(rgbFileName, os.R_OK): + err( + "Can't read the RGB file, try setting it differently using -r,\n") + err( + "Ignoring your color settings, using the defaults.\n") + useColors = 0 + if useColors: + # Colors is a list with (, ) pairs. + colors = (('barfgcolor', 'graph'), + ('barbgcolor', 'graphbg'), + ('textcolor', 'text'), + ('background', 'background')) + for key, value in colors: + col = config.get(key) + if not col is None: + code = pywmhelpers.getColorCode(col, rgbFileName) + if code is None: + err('Bad colorcode for %s, ignoring.\n' % key) + else: + pywmhelpers.setColor(xpm, value, code) + +def readConfigFile(fileName): + '''Read the config file. + + Return a list with dictionaries with the options and values in sections + [0]-[4]. + ''' + fileName = os.path.expanduser(fileName) + if not os.access(fileName, os.R_OK): + err("Can't read the configuration file %s.\n" % fileName) + # We can't do much without a configuration file + sys.exit(3) + cp = ConfigParser.ConfigParser() + try: + cp.read(fileName) + except ConfigParser.Error, e: + err("Error when reading configuration file:\n%s\n" % str(e)) + sys.exit(3) + l = [{}, {}, {}, {}, {}] + for i in range(5): + strI = str(i) + if cp.has_section(strI): + for o in cp.options(strI): + l[i][o] = cp.get(strI, o, raw=1) + return l + + +def main(): + clConfig = parseCommandLine(sys.argv) + configFile = clConfig.get('configfile', defaultConfigFile) + configFile = os.path.expanduser(configFile) + config = readConfigFile(configFile) + parseColors(defaultRGBFiles, clConfig, xpm) + try: + programName = sys.argv[0].split(os.sep)[-1] + except IndexError: + programName = '' + sys.argv[0] = programName + pywmhelpers.setDefaultPixmap(xpm) + pywmhelpers.openXwindow(sys.argv, width, height) + pywmgeneric = PywmGeneric(config) + pywmgeneric.mainLoop() + +xpm = \ +['160 100 13 1', + ' \tc #208120812081', + '.\tc #00000000FFFF', + 'o\tc #C71BC30BC71B', + 'O\tc #861782078E38', + '+\tc #EFBEF3CEEFBE', + '@\tc #618561856185', + '#\tc #9E79A2899E79', + '$\tc #410341034103', + 'o\tc #2020b2b2aaaa s indicator', + '/\tc #2020b2b2aaaa s graph', + '-\tc #707070707070 s graphbg', + 'X\tc #000000000000 s background', + '%\tc #2081B2CAAEBA s text', + ' ...............................................................................................', + ' .///..XXX..ooo..XXX..XXX.......................................................................', + ' .///..XXX..ooo..XXX..XXX.......................................................................', + ' .///..XXX..ooo..XXX..XXX.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..XXX.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..ooo.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..ooo.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..ooo.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...-------------------------------------------------------------------------------------...', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...-------------------------------------------------------------------------------------...', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...-------------------------------------------------------------------------------------...', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...-------------------------------------------------------------------------------------...', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///////////////////////////////////////////////////////////////////////////////////////////...', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///////////////////////////////////////////////////////////////////////////////////////////...', + ' .///////////////////////////////////////////////////////////////////////////////////////////...', + ' .///////////////////////////////////////////////////////////////////////////////////////////...', + ' ...............................................................................................', + ' ...............................................................................................', + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%%%%%XXX%XXX%%%%%X%%%%%X%XXX%X%%%%%X%%%%%X%%%%%X%%%%%X%%%%%XXXXXXXXXX%XXXXXXXXXX%X%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XX%%XXXXXXX%XXXXX%X%XXX%X%XXXXX%XXXXXXXXX%X%XXX%X%XXX%XX%%XXXXXX%XXXXXXXXXXXX%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XXX%XXXXXXX%XXXXX%X%XXX%X%XXXXX%XXXXXXXXX%X%XXX%X%XXX%XX%%XXXXX%%XXXXXXXXXXX%%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XXX%XXX%%%%%XX%%%%X%%%%%X%%%%%X%%%%%XXXXX%X%%%%%X%%%%%XXXXXXXXX%XXX%%%%%XXXX%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XXX%XXX%XXXXXXXXX%XXXXX%XXXXX%X%XXX%XXXXX%X%XXX%XXXXX%XXXXXXXX%%XXXXXXXXXXX%%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XXX%XXX%XXXXXXXXX%XXXXX%XXXXX%X%XXX%XXXXX%X%XXX%XXXXX%XX%%XXXX%XXXXXXXXXXXX%XXXXX%%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%%%%%XX%%%XX%%%%%X%%%%%XXXXX%X%%%%%X%%%%%XXXXX%X%%%%%X%%%%%XX%%XXXX%XXXXXXXXXXXX%X%XXX%%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + '................................................................................................................................................................', + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', + 'XX%%%XX%%%%XXX%%%%X%%%%XX%%%%XX%%%%%X%%%%%X%XXX%XXX%XXXXXXX%X%XXX%X%XXXXX%XXX%X%%%%XX%%%%%X%%%%%X%%%%%X%%%%%X%%%%%X%%%%%X%XXX%X%XXX%X%XXX%X%XXX%X%XXX%X%%%%%XXXX', + 'X%XXX%X%XXX%X%XXXXX%XXX%X%XXXXX%XXXXX%XXXXX%XXX%XXX%XXXXXXX%X%XXX%X%XXXXX%%X%%X%XXX%X%XXX%X%XXX%X%XXX%X%XXX%X%XXXXXXX%XXX%XXX%X%XXX%X%XXX%X%XXX%X%XXX%XXXXX%XXXX', + 'X%XXX%X%XXX%X%XXXXX%XXX%X%XXXXX%XXXXX%XXXXX%XXX%XXX%XXXXXXX%X%XX%XX%XXXXX%X%X%X%XXX%X%XXX%X%XXX%X%XXX%X%XXX%X%XXXXXXX%XXX%XXX%X%XXX%X%XXX%XX%X%XX%XXX%XXXX%XXXXX', + 'X%%%%%X%%%%XX%XXXXX%XXX%X%%%%XX%%%%XX%X%%%X%%%%%XXX%XXXXXXX%X%%%XXX%XXXXX%XXX%X%XXX%X%XXX%X%%%%%X%%XX%X%%%%XX%%%%%XXX%XXX%XXX%X%XXX%X%XXX%XXX%XXX%%%%%XXX%XXXXXX', + 'X%XXX%X%XXX%X%XXXXX%XXX%X%XXXXX%XXXXX%XXX%X%XXX%XXX%XXXXXXX%X%XX%XX%XXXXX%XXX%X%XXX%X%XXX%X%XXXXX%X%X%X%XXX%XXXXX%XXX%XXX%XXX%X%XXX%X%X%X%XX%X%XXXXXX%XX%XXXXXXX', + 'X%XXX%X%XXX%X%XXXXX%XXX%X%XXXXX%XXXXX%XXX%X%XXX%XXX%XXX%XXX%X%XXX%X%XXXXX%XXX%X%XXX%X%XXX%X%XXXXX%XX%%X%XXX%XXXXX%XXX%XXX%XXX%X%XXX%X%%X%%X%XXX%XXXXX%X%XXXXXXXX', + 'X%XXX%X%%%%XXX%%%%X%%%%XX%%%%XX%XXXXX%%%%%X%XXX%XXX%XXXX%%%XX%XXX%X%%%%XX%XXX%X%XXX%X%%%%%X%XXXXX%%%%%X%XXX%X%%%%%XXX%XXXX%%%%XX%%%XX%XXX%X%XXX%X%%%%%X%%%%%XXXX', + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................'] + +if __name__ == '__main__': + main() diff --git a/pywmgeneric/sample.pywmgenericrc b/pywmgeneric/sample.pywmgenericrc new file mode 100644 index 0000000..8116077 --- /dev/null +++ b/pywmgeneric/sample.pywmgenericrc @@ -0,0 +1,69 @@ +# Sample configuration file for pywmgeneric.py. +# +# THIS IS ONLY A SAMPLE. IT WILL MOST CERTAIN NOT WORK ON YOUR SYSTEM SINCE +# CUSTOM PROGRAMS ARE USED IN HERE. ONLY USE THIS AS AN REFERENCE. +# +# Available actions are: +# * method - Call a method defined in the class UserMethods. See the +# source file for more information +# * exec - Execute an external command +# +# Available mouse actions are: +# * method - Same as above, see the source file for more information. +# * exec - Same as above +# * update - Update the data through running the action. +# +# Other options: +# * scroll = yes|no - en-/disable scrolling of the text if it doens't fit. +# * display = - display a static string instead of the first line of +# the action-generated output. +# * update_delay = - the time to elapse between performing the +# action +# +# %(allText)s expands to all the text collected by the action. +# %(displayedLine)s expands to the line currently displayed in the app. This +# may show more than actually displayed since it gets the +# entire line, which may be truncated before display. +# %(allTextEscaped)s expands to the same as %(allText)s but with all ":s +# escaped to \". Great to use when passing the text as a +# command line argument to something. +# %(allTextButFirstLine)s expands to all text but leaves out the first line. +# Useful if your program for example prints a summary +# first. +# %(allTextButFirstLineEscaped)s is a combination of the two above. +# + +[0] +action = method getCpuTemp +update_delay = 10s +on_mouse1 = exec sensors | xmessage -file - +on_mouse3 = update +scroll = no + +[1] +action = method getSysTemp +update_delay = 10s +on_mouse1 = exec sensors | xmessage -file - +on_mouse3 = update +scroll = no + +[2] +action = exec tvcatcher.py -s -d +on_mouse1 = method showTvWithoutDescs +on_mouse2 = exec mozilla http://tvprogram.nu +on_mouse3 = exec xmessage "%(allTextButFirstLineEscaped)s" +update_delay = 5m +scroll = yes + +[3] +display = xterm +on_mouse1 = exec xterm + +[4] +action = exec dnheadlinecatcher.py -d +on_mouse1 = method showDnWithoutDescs +on_mouse2 = exec mozilla http://www.dn.se +on_mouse3 = exec xmessage "%(allTextEscaped)s" +update_delay = 0.5h +scroll = yes + diff --git a/pywmhdmon/INSTALL b/pywmhdmon/INSTALL new file mode 100644 index 0000000..038e3b9 --- /dev/null +++ b/pywmhdmon/INSTALL @@ -0,0 +1,8 @@ +1. If you don't have pywmgeneral installed, install it first. Can be found + at http://foo.unix.se/pywmdockapps + +2. Copy pywmhdmon.py to somewhere in your path (like ~/bin or + /usr/local/bin). + +3. Copy the sample.pywmhdmon to ~/.pywmhdmon and edit as you like. + diff --git a/pywmhdmon/README b/pywmhdmon/README new file mode 100644 index 0000000..9010aa3 --- /dev/null +++ b/pywmhdmon/README @@ -0,0 +1,16 @@ +[WHAT] +pywmhdmon is a WindowMaker dockapp that displays the available space on +up to four of your filesystems. It also contains a bar showing the +current HD activity. It currently only works on system which has a +/proc/stat like file. The application is easy to configure, invoke it +with --help or see the sample rc-file for more information. + +[WHY] +As usual, I didn't find a dockapp that fitted me. And I really like to +see the current disk activity since my disks are so quiet that I can't +hear when they work. + +[CONTACT] +Mail anything related to this program to me, Kristoffer Erlandsson, + + diff --git a/pywmhdmon/pywmhdmon.py b/pywmhdmon/pywmhdmon.py new file mode 100755 index 0000000..4d0dce0 --- /dev/null +++ b/pywmhdmon/pywmhdmon.py @@ -0,0 +1,521 @@ +#!/usr/bin/env python + +'''pywmhdmon.py + +WindowMaker dockapp to monitor the free space on your partitions and +the disk activity. + +Copyright (C) 2003 Kristoffer Erlandsson + +Licensed under the GNU General Public License. + + +Changes +2003-09-01 Kristoffer Erlandsson +Fixed a bug where the numbers wouldn't show if they were between 1000 and 1024. + +2003-06-25 Kristoffer Erlandsson +Fixed a bug where a mouse click caused the app to enter an infinite loop + +2003-06-24 Kristoffer Erlandsson +Additional fine tuning + +2003-06-23 Kristoffer Erlandsson +First working version + +''' +usage = '''pywmhdmon.py [options] +Available options are: +-h, --help print this help +-t, --textcolor set the text color +-f, --barfgcolor set the foregroundcolor of the act. bar +-g, --barbgcolor set the background color of the act. bar +-b, --background set the background color +-r, --rgbfile set the rgb file to get color codes from +-c, --configfile set the config file to use +-p, --procstat set the location of /proc/stat +''' + + +import sys +import time +import getopt +import os + +import pywmhelpers + +width = 64 +height = 64 + +xOffset = 4 +yOffset = 4 + +lettersStartX = 0 +lettersStartY = 74 +letterWidth = 6 +letterHeight = 8 + +digitsStartX = 0 +digitsStartY = 64 +digitWidth = 6 +digitHeight = 8 + +graphStartX = 7 +graphStartY = 53 +graphHeight = 4 + +graphBgStartX = 72 +graphBgStartY = 53 + +graphLineStartX = 66 +graphLineStartY = 58 + +letters = 'abcdefghijklmnopqrstuvwxyz' +digits = '0123456789:/-%. ' + +defaultConfigFile = '~/.pywmhdmonrc' +defaultRGBFiles = ('/usr/lib/X11/rgb.txt', '/usr/X11R6/lib/X11/rgb.txt') +defaultProcStat = '/proc/stat' +displayModes = ('bar', 'percent', 'free') +defaultMode = 'bar' + +class PywmHDMon: + def __init__(self, pathsToMonitor, procStat='/proc/stat', actMonEnabled=1): + self._pathsToMonitor = pathsToMonitor + self._actMonEnabled = actMonEnabled + + self._statFile = procStat + self._maxIODiff = 0 + self._lastIO = -1 + + def addString(self, s, x, y): + try: + pywmhelpers.addString(s, x, y, letterWidth, letterHeight, + lettersStartX, lettersStartY, letters, digitWidth, + digitHeight, digitsStartX, digitsStartY, digits, + xOffset, yOffset, width, height) + except ValueError, e: + sys.stderr.write('Error when painting string:\n' + str(e) + '\n') + sys.exit(3) + + def getHdInfo(self, path): + '''Get the free and total space of the filesystem which path is on. + + Return a tuple with (, ) in bytes. Raise + OSError if we can't stat the path. + These operations are quite costly, not adviced to perform these checks + more than once every 10 seconds. + ''' + stat = os.statvfs(path) + blockSize = stat.f_bsize + availableBlocks = stat.f_bavail + totalBlocks = stat.f_blocks + free = blockSize * availableBlocks + total = blockSize * totalBlocks + return (total, free) + + def paintGraph(self, percentFilled, x, y, w): + '''Paint a graph with percentFilled percent filled. + + Paint at position x, y and with width w. + ''' + paintWidth = int(round(percentFilled/100.0 * w)) + if paintWidth > 0: + pywmhelpers.copyXPMArea( + graphLineStartX, graphLineStartY, paintWidth, graphHeight, + x + xOffset, y + yOffset) + if w - paintWidth > 0: + pywmhelpers.copyXPMArea( + graphBgStartX, graphBgStartY, w - paintWidth, graphHeight, + x + paintWidth + xOffset, y + yOffset) + + def getY(self, line): + return 2 + (line - 1) * (letterHeight + 3) + + def paintLabel(self, line, label): + self.addString(label, 1, self.getY(line)) + + def paintHdData(self, line, data, mode): + total, free = data + xStart = width - xOffset - 6 * letterWidth - 1 + if mode == 'percent': + percent = (float(free) / float(total)) * 100.0 + percentStr = (str(int(round(percent))) + '%').rjust(5) + self.addString(percentStr, xStart, self.getY(line)) + elif mode == 'free': + freeStr = bytesToStr(free).rjust(5) + self.addString(freeStr, xStart, self.getY(line)) + elif mode == 'bar': + percentUsed = (float(total - free) / float(total)) * 100.0 + self.paintGraph(percentUsed, xStart, self.getY(line) + 2, + width - xOffset*2 - xStart - 2) + else: + sys.stderr.write('Unknown display mode: %s, ignoring data.\n' + % mode) + def getHdActivity(self): + '''Return the current hd activity in percent. + + Return how many percent of the max achieved activity during the + program's lifetime the current activity is. However, every time + this method is called we decrease the max achieved activity a + little bit to get a bit less affected by spikes. I think the + interesting thing is to see if the hard drive is active, not + really exactly how active. + ''' + statFile = file(self._statFile, 'r') + diskIoStartTag = 'disk_io: ' + ioLine = None + for line in statFile: + if line.startswith(diskIoStartTag): + ioLine = line + statFile.close() + if ioLine is None: + # Can't get HD activity + sys.stderr.write("Can't get hd activity from %s\n" % + self._statFile) + return 0.0 + ioLine = ioLine[len(diskIoStartTag):] + disks = ioLine.split() + currIO = 0 + for disk in disks: + dataPart = disk.split(':')[1].strip(')(') + infos = dataPart.split(',') + blocksRead = long(infos[2]) + blocksWritten = long(infos[4]) + currIO += blocksRead + blocksWritten + if self._lastIO == -1: + self._lastIO = currIO + currDiff = currIO - self._lastIO + self._lastIO = currIO + if currDiff > self._maxIODiff: + self._maxIODiff = currDiff + if self._maxIODiff <= 0: + self._maxIODiff = 0 + return 0.0 + currAct = (float(currDiff) / float(self._maxIODiff)) * 100.0 + self._maxIODiff -= 1 # So spikes won't affect us too much. + return currAct + + def updateHdActivity(self): + currentAct = self.getHdActivity() + self.paintGraph(currentAct, 3, height - yOffset*2 - 3 - graphHeight, + width - 2 * xOffset - 6) + + def _checkEvents(self): + event = pywmhelpers.getEvent() + while not event is None: + if event['type'] == 'destroynotify': + sys.exit(0) + event = pywmhelpers.getEvent() + + def mainLoop(self): + counter = -1 + while 1: + counter += 1 + self._checkEvents() + if self._actMonEnabled: + self.updateHdActivity() + if counter % 100 == 0: + index = 0 + for i in self._pathsToMonitor: + if not i is None: + label, path, mode = i + self.paintLabel(index + 1, label) + try: + hdData = self.getHdInfo(path) + except OSError, e: + sys.stderr.write( + "Can't get hd data from %s: %s\n" % (path, str(e))) + hdData = (0, 0) + self.paintHdData(index + 1, hdData, mode) + index += 1 + if counter == 9999999: + counter = -1 + pywmhelpers.redraw() + time.sleep(0.1) + + + + +def parseCommandLine(argv): + '''Parse the commandline. Return a dictionary with options and values.''' + shorts = 'ht:f:g:b:r:c:p:' + longs = ['help', 'textcolor=', 'background=', 'barfgcolor=', + 'rgbfile=', 'configfile=', 'barbgcolor=', 'procstat='] + try: + opts, nonOptArgs = getopt.getopt(argv[1:], shorts, longs) + except getopt.GetoptError, e: + sys.stderr.write('Error when parsing commandline: ' + str(e) + '\n') + sys.stderr.write(usage) + sys.exit(2) + d = {} + for o, a in opts: + if o in ('-h', '--help'): + sys.stdout.write(usage) + sys.exit(0) + if o in ('-t', '--textcolor'): + d['textcolor'] = a + if o in ('-b', '--background'): + d['background'] = a + if o in ('-r', '--rgbfile'): + d['rgbfile'] = a + if o in ('-c', '--configfile'): + d['configfile'] = a + if o in ('-g', '--barbgcolor'): + d['barbgcolor'] = a + if o in ('-f', '--barfgcolor'): + d['barfgcolor'] = a + if o in ('-p', '--procstat'): + d['procstat'] = a + return d + +def parseColors(defaultRGBFileList, config, xpm): + rgbFileName = '' + for fn in defaultRGBFileList: + if os.access(fn, os.R_OK): + rgbFileName = fn + break + rgbFileName = config.get('rgbfile', rgbFileName) + useColors = 1 + if not os.access(rgbFileName, os.R_OK): + sys.stderr.write( + "Can't read the RGB file, try setting it differently using -r,\n") + sys.stderr.write( + "Ignoring your color settings, using the defaults.\n") + useColors = 0 + if useColors: + # Colors is a list with (, ) pairs. + colors = (('barfgcolor', 'graph'), + ('barbgcolor', 'graphbg'), + ('textcolor', 'text'), + ('background', 'background')) + for key, value in colors: + col = config.get(key) + if not col is None: + code = pywmhelpers.getColorCode(col, rgbFileName) + if code is None: + sys.stderr.write('Bad colorcode for %s, ignoring.\n' % key) + else: + pywmhelpers.setColor(xpm, value, code) + +def makeNumDigits(num, numDigits): + '''Make a floating point number a certain number of digits, including + decimal. Return a string containing it. + ''' + lenOfIntPart = len(str(int(num))) + if lenOfIntPart > numDigits: + # Can't convert a number to less digits then it's integer part... + return '' + decimalsNeeded = numDigits - lenOfIntPart + s = '%' + str(lenOfIntPart) + '.' + str(decimalsNeeded) + 'f' + s = s % round(num, decimalsNeeded) + return s + +def bytesToStr(bytes): + '''Convert a number of bytes to a nice printable string. + + May raise ValueError if bytes can't be seen as an float. + ''' + bytes = float(bytes) + kb = 1024 + mb = 1024 * 1024 + gb = 1024 * mb + tb = 1024 * gb + pb = 1024 * tb + if bytes < kb: + size = bytes + letter = 'B' + #return makeNumDigits(bytes, numDigits) + 'B' + elif bytes < mb: + size = bytes / kb + letter = 'k' + #return makeNumDigits(bytes/kb, numDigits) + 'k' + elif bytes < gb: + size = bytes / mb + letter = 'M' + #return makeNumDigits(bytes/mb, numDigits) + 'M' + elif bytes < tb: + size = bytes / gb + letter = 'G' + #return makeNumDigits(bytes/gb, numDigits) + 'G' + elif bytes < pb: + size = bytes / tb + letter = 'T' + #return makeNumDigits(bytes/tb, numDigits) + 'T' + else: + size = bytes / pb + letter = 'p' + #return makeNumDigits(bytes/pb, numDigits) + 'P' + if size >= 1000: + res = makeNumDigits(size, 4) + else: + res = makeNumDigits(size, 3) + res += letter + return res + + +def main(): + clConfig = parseCommandLine(sys.argv) + configFile = clConfig.get('configfile', defaultConfigFile) + configFile = os.path.expanduser(configFile) + fileConfig = pywmhelpers.readConfigFile(configFile, sys.stderr) + config = fileConfig + for i in clConfig.iteritems(): + config[i[0]] = i[1] + parseColors(defaultRGBFiles, config, xpm) + + pathsToMonitor = [] + for i in range(1,5): + labelStr = str(i) + '.label' + pathStr = str(i) + '.path' + modeStr = str(i) + '.displaymode' + label = config.get(labelStr) + path = config.get(pathStr) + displayMode = config.get(modeStr, defaultMode) + if not displayMode in displayModes: + sys.stderr.write( + 'Unknown display mode: %s, using default.\n' % displayMode) + displayMode = defaultMode + if label is None or path is None: + pathsToMonitor.append(None) + else: + pathsToMonitor.append((label[:3], path, displayMode)) + procStat = config.get('procstat', defaultProcStat) + actMonEnabled = 1 + if not os.access(procStat, os.R_OK): + sys.stderr.write( + "Can't read your procstat file, try setting it with -p. ") + sys.stderr.write("Disabling the HD activity bar.\n") + actMonEnabled = 0 + try: + programName = sys.argv[0].split(os.sep)[-1] + except IndexError: + programName = '' + sys.argv[0] = programName + pywmhelpers.setDefaultPixmap(xpm) + pywmhelpers.openXwindow(sys.argv, width, height) + # XXX Add commands for clicking different areas? + hdmon = PywmHDMon(pathsToMonitor, procStat, actMonEnabled) + hdmon.mainLoop() + + +xpm = \ +['160 100 13 1', + ' \tc #208120812081', + '.\tc #00000000FFFF', + 'o\tc #C71BC30BC71B', + 'O\tc #861782078E38', + '+\tc #EFBEF3CEEFBE', + '@\tc #618561856185', + '#\tc #9E79A2899E79', + '$\tc #410341034103', + 'o\tc #2020b2b2aaaa s indicator', + '/\tc #2020b2b2aaaa s graph', + '-\tc #707070707070 s graphbg', + 'X\tc #000000000000 s background', + '%\tc #2081B2CAAEBA s text', + ' ...............................................................................................', + ' .///..XXX..ooo..XXX..XXX.......................................................................', + ' .///..XXX..ooo..XXX..XXX.......................................................................', + ' .///..XXX..ooo..XXX..XXX.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..XXX.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..XXX.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..XXX.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..ooo..XXX.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..ooo..XXX.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..ooo..XXX.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..XXX.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..XXX.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..XXX.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..ooo.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..ooo.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..ooo.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...-------------------------------------------------------------------------------------...', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...-------------------------------------------------------------------------------------...', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...-------------------------------------------------------------------------------------...', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...-------------------------------------------------------------------------------------...', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///////////////////////////////////////////////////////////////////////////////////////////...', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///////////////////////////////////////////////////////////////////////////////////////////...', + ' .///////////////////////////////////////////////////////////////////////////////////////////...', + ' .///////////////////////////////////////////////////////////////////////////////////////////...', + ' ...............................................................................................', + ' ...............................................................................................', + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%%%%%XXX%XXX%%%%%X%%%%%X%XXX%X%%%%%X%%%%%X%%%%%X%%%%%X%%%%%XXXXXXXXXX%XXXXXXXXXX%X%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XX%%XXXXXXX%XXXXX%X%XXX%X%XXXXX%XXXXXXXXX%X%XXX%X%XXX%XX%%XXXXXX%XXXXXXXXXXXX%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XXX%XXXXXXX%XXXXX%X%XXX%X%XXXXX%XXXXXXXXX%X%XXX%X%XXX%XX%%XXXXX%%XXXXXXXXXXX%%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XXX%XXX%%%%%XX%%%%X%%%%%X%%%%%X%%%%%XXXXX%X%%%%%X%%%%%XXXXXXXXX%XXX%%%%%XXXX%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XXX%XXX%XXXXXXXXX%XXXXX%XXXXX%X%XXX%XXXXX%X%XXX%XXXXX%XXXXXXXX%%XXXXXXXXXXX%%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XXX%XXX%XXXXXXXXX%XXXXX%XXXXX%X%XXX%XXXXX%X%XXX%XXXXX%XX%%XXXX%XXXXXXXXXXXX%XXXXX%%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%%%%%XX%%%XX%%%%%X%%%%%XXXXX%X%%%%%X%%%%%XXXXX%X%%%%%X%%%%%XX%%XXXX%XXXXXXXXXXXX%X%XXX%%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + '................................................................................................................................................................', + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', + 'XX%%%XX%%%%XXX%%%%X%%%%XX%%%%XX%%%%%X%%%%%X%XXX%XXX%XXXXXXX%X%XXX%X%XXXXX%XXX%X%%%%XX%%%%%X%%%%%X%%%%%X%%%%%X%%%%%X%%%%%X%XXX%X%XXX%X%XXX%X%XXX%X%XXX%X%%%%%XXXX', + 'X%XXX%X%XXX%X%XXXXX%XXX%X%XXXXX%XXXXX%XXXXX%XXX%XXX%XXXXXXX%X%XXX%X%XXXXX%%X%%X%XXX%X%XXX%X%XXX%X%XXX%X%XXX%X%XXXXXXX%XXX%XXX%X%XXX%X%XXX%X%XXX%X%XXX%XXXXX%XXXX', + 'X%XXX%X%XXX%X%XXXXX%XXX%X%XXXXX%XXXXX%XXXXX%XXX%XXX%XXXXXXX%X%XX%XX%XXXXX%X%X%X%XXX%X%XXX%X%XXX%X%XXX%X%XXX%X%XXXXXXX%XXX%XXX%X%XXX%X%XXX%XX%X%XX%XXX%XXXX%XXXXX', + 'X%%%%%X%%%%XX%XXXXX%XXX%X%%%%XX%%%%XX%X%%%X%%%%%XXX%XXXXXXX%X%%%XXX%XXXXX%XXX%X%XXX%X%XXX%X%%%%%X%%XX%X%%%%XX%%%%%XXX%XXX%XXX%X%XXX%X%XXX%XXX%XXX%%%%%XXX%XXXXXX', + 'X%XXX%X%XXX%X%XXXXX%XXX%X%XXXXX%XXXXX%XXX%X%XXX%XXX%XXXXXXX%X%XX%XX%XXXXX%XXX%X%XXX%X%XXX%X%XXXXX%X%X%X%XXX%XXXXX%XXX%XXX%XXX%X%XXX%X%X%X%XX%X%XXXXXX%XX%XXXXXXX', + 'X%XXX%X%XXX%X%XXXXX%XXX%X%XXXXX%XXXXX%XXX%X%XXX%XXX%XXX%XXX%X%XXX%X%XXXXX%XXX%X%XXX%X%XXX%X%XXXXX%XX%%X%XXX%XXXXX%XXX%XXX%XXX%X%XXX%X%%X%%X%XXX%XXXXX%X%XXXXXXXX', + 'X%XXX%X%%%%XXX%%%%X%%%%XX%%%%XX%XXXXX%%%%%X%XXX%XXX%XXXX%%%XX%XXX%X%%%%XX%XXX%X%XXX%X%%%%%X%XXXXX%%%%%X%XXX%X%%%%%XXX%XXXX%%%%XX%%%XX%XXX%X%XXX%X%%%%%X%%%%%XXXX', + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................'] + + +if __name__ == '__main__': + main() + diff --git a/pywmhdmon/sample.pywmhdmon b/pywmhdmon/sample.pywmhdmon new file mode 100644 index 0000000..c618201 --- /dev/null +++ b/pywmhdmon/sample.pywmhdmon @@ -0,0 +1,26 @@ +[DEFAULT] +# Displaymodes are free, percent or bar, displays the free size, free percent +# and a bar representing space, respectively. +1.label=/ +1.path=/ +1.displaymode=free + +2.label=c +2.path=/mnt/c +2.displaymode=free + +3.label=d +3.path=/mnt/d +3.displaymode=free + +4.label=e +4.path=/mnt/e +4.displaymode=free + +textcolor=light sea green +background=black +barfgcolor=light sea green +barbgcolor=grey45 + +#rgbfile=/usr/lib/X11/rgb.txt + diff --git a/pywmseti/INSTALL b/pywmseti/INSTALL new file mode 100644 index 0000000..fff976e --- /dev/null +++ b/pywmseti/INSTALL @@ -0,0 +1,8 @@ +1. If you don't have pywmgeneral installed, install it first. Can be found + at http://www.cyd.liu.se/~krier115/pywm/ + +2. Copy pywmseti.py to somewhere in your path (like ~/bin or + /usr/local/bin). + +3. Copy the sample.pywmseti to ~/.pywmseti and edit as you like. + diff --git a/pywmseti/README b/pywmseti/README new file mode 100644 index 0000000..34286ab --- /dev/null +++ b/pywmseti/README @@ -0,0 +1,17 @@ +[WHAT] +Pywmseti is an WindowMaker dockapp for monitoring your seti@home progress. +The application displays how many workunits you have done and the progress +on the current one. You start/stop the seti@home process by simply clicking +anywhere in the application. It also displays the time spent on the workunit +(or since you started the program if you restart it in the middle of a +workunit). Invoke the program with --help or see the sample rc-file for +more information about customization. + +[WHY] +No one of the existing monitors suited my needs. And the more Python the +world gets, the better it'll be! ;) + +[CONTACT] +Mail anything related to this program to me, Kristoffer Erlandsson, + + diff --git a/pywmseti/pywmseti.py b/pywmseti/pywmseti.py new file mode 100755 index 0000000..fb58455 --- /dev/null +++ b/pywmseti/pywmseti.py @@ -0,0 +1,551 @@ +#!/usr/bin/env python + +'''pywmseti.py + +WindowMaker dockapp to monitor the progress of your seti@home. + +Copyright (C) 2003 Kristoffer Erlandsson + +Licensed under the GNU General Public License. + + +Changes + +2003-06-24 Kristoffer Erlandsson +Added event handling for graceful shutdown + +2003-06-17 Kristoffer Erlandsson +First workingish version + +''' +usage = '''pywmseti.py [options] +Available options are: +-h, --help print this help +-t, --textcolor set the text color +-p, --progressbarcolor set the color of the progress bar +-g, --barbgcolor set the background color of the progress bar +-i, --indicatorcolor set the color of the running indicator +-b, --background set the background color +-d, --setidir set the directory where seti@home resides +-n, --nice set the nice value to run seti@home with +-r, --rgbfile set the rgb file to get color codes from +-c, --configfile set the config file to use +''' + +import sys +import time +import getopt +import os + +import pywmhelpers + +width = 64 +height = 64 + +xOffset = 4 +yOffset = 4 + +lettersStartX = 0 +lettersStartY = 74 +letterWidth = 6 +letterHeight = 8 + +digitsStartX = 0 +digitsStartY = 64 +digitWidth = 6 +digitHeight = 8 + +graphStartX = 7 +graphStartY = 53 +graphLength = 50 +graphHeight = 4 + +graphBgStartX = 72 +graphBgStartY = 53 + +graphLineStartX = 66 +graphLineStartY = 58 + +runningIndX = 71 +runningIndY = 1 +runningIndWidth = 3 +runningIndHeight = 15 +numRunningInds = 4 + +letters = 'abcdefghijklmnopqrstuvwxyz' +digits = '0123456789:/-% ' + +defaultConfigFile = '~/.pywmsetirc' +defaultRGBFiles = ['/usr/lib/X11/rgb.txt', '/usr/X11R6/lib/X11/rgb.txt'] + +stateFileName = 'state.sah' +uinfoFileName = 'user_info.sah' +pidFileName = 'pid.sah' +execFileName = 'setiathome' + +class PywmSeti: + def __init__(self, statePath, uinfoPath, pidPath, execCmd): + self._statePath = statePath + self._uinfoPath = uinfoPath + self._pidPath = pidPath + self._execCmd = execCmd + self._currentRunningInd = 0 + self._lastTime = time.time() + self._lastNumResults = -1 + self._progress = 0 + + def addString(self, s, x, y): + try: + pywmhelpers.addString(s, x, y, letterWidth, letterHeight, + lettersStartX, lettersStartY, letters, digitWidth, + digitHeight, digitsStartX, digitsStartY, digits, + xOffset, yOffset, width, height) + except ValueError, e: + sys.stderr.write('Error when painting string:\n' + str(e) + '\n') + sys.exit(3) + + def getCenterStartPos(self, s): + return pywmhelpers.getCenterStartPos(s, letterWidth, width, xOffset) + + def getVertSpacing(self, numLines, margin): + return pywmhelpers.getVertSpacing(numLines, margin, height, + letterHeight, yOffset) + + def getProgress(self, lines): + '''Return the progess of the current workunit. + + Supply the lines of the statefile as argument. + ''' + for line in lines: + if line.startswith('prog='): + try: + progress = float(line.split('=')[-1]) + except ValueError: + progress = 0 + return progress + return 0 + + def getNumResults(self, lines): + '''Return the number of results produced. + + Supply the lines in the user info file as argument. + ''' + for line in lines: + if line.startswith('nresults='): + try: + results = int(line.split('=')[-1]) + except ValueError: + pass + else: + return results + sys.stderr.write( + "Error when reading uinfo file! Can't get number of results.\n") + return -1 + + def pidIsRunning(self, pid): + '''Determine if the process with PID pid is running. + + Return 1 if it is running. + Return 0 if it is not running. + Return -1 if we do not have permission to signal the process + This could be slightly non-portal, but I can not find any better + way to do it. + ''' + try: + os.kill(pid, 0) + except OSError, e: + if e.errno == 1: + return -1 + return 0 + return 1 + + def openFileRead(self, fileName): + try: + f = file(fileName, 'r') + except IOError, e: + sys.stderr.write('Error when opening %s: %s\n' % (fileName, str(e))) + return None + return f + + + def paintCurrentRunningIndicator(self): + '''Paint the running indicator. + ''' + indX = runningIndX + self._currentRunningInd * \ + (runningIndWidth + 2) + indY = runningIndY + w = runningIndWidth + h = runningIndHeight + targX = width - xOffset - w - 5 + targY = yOffset + 5 + pywmhelpers.copyXPMArea(indX, indY, w, h, targX, targY) + + def updateRunning(self): + '''Update the information regarding if we got seti@home running or not. + + Return a tuple with (running, startStopenabled). + startStopEnabled is 1 if we own the process and got the permissions + to start and stop it, or if there is no process running. + ''' + pidFile = self.openFileRead(self._pidPath) + if pidFile is None: + sys.stderr.write("Can't read pid file") + self._running = 0 + self._startStopEnabled = 0 + return + try: + self._pid = int(pidFile.read().strip()) + except ValueError: + sys.stderr.write("Can't get pid from %s.\n" % self._pidPath) + self._running = 0 + self._startStopEnabled = 0 + return + pidFile.close() + self._running = self.pidIsRunning(self._pid) + if self._running == -1 and self._startStopEnabled: + sys.stderr.write( + "An other seti@home process which you don't own is running.\n") + sys.stderr.write( + "Starting and stopping of the process is disabled.\n") + self._startStopenabled = 0 + if self._running == -1: + self._running = 1 + else: + # If no process is running (we could have stopped the one + # running from an other process), enable starting and stopping. + self._startStopEnabled = 1 + if self._running: + self._currentRunningInd = (self._currentRunningInd - 1) \ + % numRunningInds + else: + self._currentRunningInd = 0 + self.paintCurrentRunningIndicator() + + def updateProgress(self): + '''Update the progress on the current workunit.''' + stateFile = self.openFileRead(self._statePath) + if stateFile is None: + # Can't open file, probably in progress of gettin a new workunit. + progress = 0 + else: + progress = self.getProgress(stateFile.readlines()) + stateFile.close() + self._progress = progress + percent = int(progress * 100.0) + graphSize = int(round(progress * graphLength)) + pywmhelpers.copyXPMArea( + graphLineStartX, graphLineStartY, graphSize, graphHeight, + graphStartX, graphStartY) + pywmhelpers.copyXPMArea( + graphBgStartX, graphBgStartY, graphLength - graphSize, graphHeight, + graphStartX + graphSize, graphStartY) + self.addString((str(percent) + '%').ljust(4), 4, 32) + + def updateNumResults(self): + '''Update the number of workunits done.''' + uinfoFile = self.openFileRead(self._uinfoPath) + numResults = self.getNumResults(uinfoFile.readlines()) + if self._lastNumResults == -1: + self._lastNumResults = numResults + if numResults != self._lastNumResults and self._progress < 0.03: + # If we just got a new number of results and the progress of the + # current workunit is under 3%, assume we started working on a new + # workunit. The times this could be missleading is if we have an + # other seti@home process running on an other computer, but this is + # accurate enough I think. + self.nextWorkUnitStarted() + self._lastNumResults = numResults + uinfoFile.close() + self.addString(str(numResults)[:7], 4, 4) + + def updateTime(self): + '''Update the time line. + + We display the time that we have been on the current work unit, since + either the last one was done or since we started the program. + ''' + timeSpent = time.time() - self._lastTime + hours = int(timeSpent / 3600) + mins = int((timeSpent - hours * 3600) / 60) + hours = str(hours)[:3] + mins = str(mins).zfill(2) + s = (hours + ':' + mins).ljust(6) + self.addString(s, 4, 18) + + def nextWorkUnitStarted(self): + self._lastTime = time.time() + + def handleMouseClick(self, region): + if region == 0: + if self._startStopEnabled: + if self._running: + try: + os.kill(self._pid, 15) + except OSError, e: + sys.stderr.write( + "Error when ending process: "+str(e)+'\n') + else: + os.system(self._execCmd) # Use fork instead? + + def _checkForEvents(self): + '''Check for, and handle, X events.''' + event = pywmhelpers.getEvent() + while not event is None: + if event['type'] == 'buttonrelease': + region = pywmhelpers.checkMouseRegion(event['x'], + event['y']) + self.handleMouseClick(region) + elif event['type'] == 'destroynotify': + sys.exit(0) + event = pywmhelpers.getEvent() + + def mainLoop(self): + counter = -1 + self._startStopEnabled = 1 + while 1: + counter += 1 + self._checkForEvents() + if counter % 10 == 0: + self.updateRunning() + if counter % 100 == 0: + self.updateProgress() + self.updateNumResults() + self.updateTime() + if counter == 999999: + counter = -1 + pywmhelpers.redraw() + time.sleep(0.1) + + +def parseCommandLine(argv): + '''Parse the commandline. Return a dictionary with options and values.''' + shorts = 'ht:b:n:d:r:c:p:g:i:' + longs = ['help', 'textcolor=', 'background=', 'setidir=', 'nice=', + 'rgbfile=', 'configfile=', 'progressbarcolor=', 'barbgcolor=', + 'indicatorcolor='] + try: + opts, nonOptArgs = getopt.getopt(argv[1:], shorts, longs) + except getopt.GetoptError, e: + sys.stderr.write('Error when parsing commandline: ' + str(e) + '\n') + sys.stderr.write(usage) + sys.exit(2) + d = {} + for o, a in opts: + if o in ('-h', '--help'): + sys.stdout.write(usage) + sys.exit(0) + if o in ('-t', '--textcolor'): + d['textcolor'] = a + if o in ('-b', '--background'): + d['background'] = a + if o in ('-d', '--setidir'): + d['setidir'] = a + if o in ('-n', '--nice'): + d['nice'] = a + if o in ('-r', '--rgbfile'): + d['rgbfile'] = a + if o in ('-c', '--configfile'): + d['configfile'] = a + if o in ('-p', '--progressbarcolor'): + d['progressbarcolor'] = a + if o in ('-g', '--barbgcolor'): + d['barbgcolor'] = a + if o in ('-i', '--indicatorcolor'): + d['indicatorcolor'] = a + return d + +def parseColors(defaultRGBFileNames, config, xpm): + rgbFileName = '' + for fn in defaultRGBFileNames: + if os.access(fn, os.R_OK): + rgbFileName = fn + break + rgbFileName = config.get('rgbfile', rgbFileName) + useColors = 1 + if not os.access(rgbFileName, os.R_OK): + sys.stderr.write( + "Can't read the RGB file, try setting it differently using -r,\n") + sys.stderr.write( + "Ignoring your color settings, using the defaults.\n") + useColors = 0 + if useColors: + # Colors is a list with (, ) pairs. + colors = (('indicatorcolor', 'indicator'), + ('progressbarcolor', 'graph'), + ('barbgcolor', 'graphbg'), + ('textcolor', 'text'), + ('background', 'background')) + for key, value in colors: + col = config.get(key) + if not col is None: + code = pywmhelpers.getColorCode(col, rgbFileName) + if code is None: + sys.stderr.write('Bad colorcode for %s, ignoring.\n' % key) + else: + pywmhelpers.setColor(xpm, value, code) + +def main(): + clConfig = parseCommandLine(sys.argv) + configFile = clConfig.get('configfile', defaultConfigFile) + configFile = os.path.expanduser(configFile) + fileConfig = pywmhelpers.readConfigFile(configFile, sys.stderr) + # Merge the two configs, let the commandline options overwrite those in the + # configuration file. + config = fileConfig + for i in clConfig.iteritems(): + config[i[0]] = i[1] + # Get the configurations + parseColors(defaultRGBFiles, config, xpm) + setiDir = config.get('setidir') + if setiDir is None: + sys.stderr.write( + 'You have to supply a directory where seti@home resides. Either in\n') + sys.stderr.write( + 'the configuration file or with -d/--setidir.\n') + sys.exit(3) + setiDir = os.path.expanduser(setiDir) + try: + os.chdir(setiDir) + except OSError, e: + sys.stderr.write('Error when accessing seti directory: %s\n' % str(e)) + sys.exit(4) + statePath = os.path.join(setiDir, stateFileName) + uinfoPath = os.path.join(setiDir, uinfoFileName) + pidPath = os.path.join(setiDir, pidFileName) + execPath = os.path.join(setiDir, execFileName) + niceVal = config.get('nice') + if niceVal is None: + execCmd = execPath + else: + execCmd = execPath + ' -nice %s' % niceVal + '&' + try: + programName = sys.argv[0].split(os.sep)[-1] + except IndexError: + programName = '' + sys.argv[0] = programName + pywmhelpers.setDefaultPixmap(xpm) + pywmhelpers.openXwindow(sys.argv, width, height) + pywmhelpers.addMouseRegion(0, xOffset, yOffset, width - 2 * xOffset, + height - 2 * yOffset) + pwms = PywmSeti(statePath, uinfoPath, pidPath, execCmd) + pwms.mainLoop() + +xpm = \ +['160 100 13 1', + ' \tc #208120812081', + '.\tc #00000000FFFF', + 'o\tc #C71BC30BC71B', + 'O\tc #861782078E38', + '+\tc #EFBEF3CEEFBE', + '@\tc #618561856185', + '#\tc #9E79A2899E79', + '$\tc #410341034103', + 'o\tc #2020b2b2aaaa s indicator', + '/\tc #2020b2b2aaaa s graph', + '-\tc #707070707070 s graphbg', + 'X\tc #000000000000 s background', + '%\tc #2081B2CAAEBA s text', + ' ...............................................................................................', + ' .///..XXX..ooo..XXX..XXX.......................................................................', + ' .///..XXX..ooo..XXX..XXX.......................................................................', + ' .///..XXX..ooo..XXX..XXX.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..XXX.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..XXX.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..XXX.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..ooo..XXX.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..ooo..XXX.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..ooo..XXX.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..XXX.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..XXX.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..XXX.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..ooo.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..ooo.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..ooo.......................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...-------------------------------------------------------------------------------------...', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...-------------------------------------------------------------------------------------...', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...-------------------------------------------------------------------------------------...', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...-------------------------------------------------------------------------------------...', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///////////////////////////////////////////////////////////////////////////////////////////...', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///////////////////////////////////////////////////////////////////////////////////////////...', + ' .///////////////////////////////////////////////////////////////////////////////////////////...', + ' .///////////////////////////////////////////////////////////////////////////////////////////...', + ' ...............................................................................................', + ' ...............................................................................................', + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%%%%%XXX%XXX%%%%%X%%%%%X%XXX%X%%%%%X%%%%%X%%%%%X%%%%%X%%%%%XXXXXXXXXX%XXXXXXXXXX%X%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XX%%XXXXXXX%XXXXX%X%XXX%X%XXXXX%XXXXXXXXX%X%XXX%X%XXX%XX%%XXXXXX%XXXXXXXXXXXX%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XXX%XXXXXXX%XXXXX%X%XXX%X%XXXXX%XXXXXXXXX%X%XXX%X%XXX%XX%%XXXXX%%XXXXXXXXXXX%%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XXX%XXX%%%%%XX%%%%X%%%%%X%%%%%X%%%%%XXXXX%X%%%%%X%%%%%XXXXXXXXX%XXX%%%%%XXXX%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XXX%XXX%XXXXXXXXX%XXXXX%XXXXX%X%XXX%XXXXX%X%XXX%XXXXX%XXXXXXXX%%XXXXXXXXXXX%%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XXX%XXX%XXXXXXXXX%XXXXX%XXXXX%X%XXX%XXXXX%X%XXX%XXXXX%XX%%XXXX%XXXXXXXXXXXX%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%%%%%XX%%%XX%%%%%X%%%%%XXXXX%X%%%%%X%%%%%XXXXX%X%%%%%X%%%%%XX%%XXXX%XXXXXXXXXXXX%X%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + '................................................................................................................................................................', + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', + 'XX%%%XX%%%%XXX%%%%X%%%%XX%%%%XX%%%%%X%%%%%X%XXX%XXX%XXXXXXX%X%XXX%X%XXXXX%XXX%X%%%%XX%%%%%X%%%%%X%%%%%X%%%%%X%%%%%X%%%%%X%XXX%X%XXX%X%XXX%X%XXX%X%XXX%X%%%%%XXXX', + 'X%XXX%X%XXX%X%XXXXX%XXX%X%XXXXX%XXXXX%XXXXX%XXX%XXX%XXXXXXX%X%XXX%X%XXXXX%%X%%X%XXX%X%XXX%X%XXX%X%XXX%X%XXX%X%XXXXXXX%XXX%XXX%X%XXX%X%XXX%X%XXX%X%XXX%XXXXX%XXXX', + 'X%XXX%X%XXX%X%XXXXX%XXX%X%XXXXX%XXXXX%XXXXX%XXX%XXX%XXXXXXX%X%XX%XX%XXXXX%X%X%X%XXX%X%XXX%X%XXX%X%XXX%X%XXX%X%XXXXXXX%XXX%XXX%X%XXX%X%XXX%XX%X%XX%XXX%XXXX%XXXXX', + 'X%%%%%X%%%%XX%XXXXX%XXX%X%%%%XX%%%%XX%X%%%X%%%%%XXX%XXXXXXX%X%%%XXX%XXXXX%XXX%X%XXX%X%XXX%X%%%%%X%%XX%X%%%%XX%%%%%XXX%XXX%XXX%X%XXX%X%XXX%XXX%XXX%%%%%XXX%XXXXXX', + 'X%XXX%X%XXX%X%XXXXX%XXX%X%XXXXX%XXXXX%XXX%X%XXX%XXX%XXXXXXX%X%XX%XX%XXXXX%XXX%X%XXX%X%XXX%X%XXXXX%X%X%X%XXX%XXXXX%XXX%XXX%XXX%X%XXX%X%X%X%XX%X%XXXXXX%XX%XXXXXXX', + 'X%XXX%X%XXX%X%XXXXX%XXX%X%XXXXX%XXXXX%XXX%X%XXX%XXX%XXX%XXX%X%XXX%X%XXXXX%XXX%X%XXX%X%XXX%X%XXXXX%XX%%X%XXX%XXXXX%XXX%XXX%XXX%X%XXX%X%%X%%X%XXX%XXXXX%X%XXXXXXXX', + 'X%XXX%X%%%%XXX%%%%X%%%%XX%%%%XX%XXXXX%%%%%X%XXX%XXX%XXXX%%%XX%XXX%X%%%%XX%XXX%X%XXX%X%%%%%X%XXXXX%%%%%X%XXX%X%%%%%XXX%XXXX%%%%XX%%%XX%XXX%X%XXX%X%%%%%X%%%%%XXXX', + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................'] + + +if __name__ == '__main__': + main() diff --git a/pywmseti/sample.pywmsetirc b/pywmseti/sample.pywmsetirc new file mode 100644 index 0000000..da86ee4 --- /dev/null +++ b/pywmseti/sample.pywmsetirc @@ -0,0 +1,12 @@ +[DEFAULT] +setidir=/home/erl/setiathome +# Nice value to start the seti@home process with on click. +nice=19 +textcolor=light sea green +progressbarcolor=light sea green +barbgcolor=grey45 +indicatorcolor=light sea green +background=black + +#rgbfile=/usr/lib/X11/rgb.txt + diff --git a/pywmsysmon/INSTALL b/pywmsysmon/INSTALL new file mode 100644 index 0000000..85e52b3 --- /dev/null +++ b/pywmsysmon/INSTALL @@ -0,0 +1,6 @@ +1. If you don't have pywmgeneral installed, install it first. Can be found + at http://foo.unix.se/pywmdockapps + +2. Copy pywmsysmon.py to somewhere in your path (like ~/bin or + /usr/local/bin). + diff --git a/pywmsysmon/README b/pywmsysmon/README new file mode 100644 index 0000000..b66110f --- /dev/null +++ b/pywmsysmon/README @@ -0,0 +1,15 @@ +[WHAT] +pywmsysmon is a WindowMaker dockapp that displays your cpu and memory +usages. The upper graph shows your cpu usage history and the lower +"progress bar" shows your current memory usage excluding cached and +buffered data. This program currently only works on systems which got +the /proc/stat and /proc/meminfo files available. Invoke the program +with --help for information about customization. + +[WHY] +As usual, I didn't find a dockapp that fitted me. + +[CONTACT] +Mail anything related to this program to me, Kristoffer Erlandsson, + + diff --git a/pywmsysmon/pywmsysmon.py b/pywmsysmon/pywmsysmon.py new file mode 100755 index 0000000..16385ee --- /dev/null +++ b/pywmsysmon/pywmsysmon.py @@ -0,0 +1,465 @@ +#! /usr/bin/env python + +'''pywmsysmon.py + +WindowMaker system monitor dockapp written in Python. It displays your CPU +usage and your available/used memory. + +Copyright (C) 2003 Kristoffer Erlandsson + +Licensed under the GNU General Public License. + +Changes +2003-06-28 Kristoffer Erlandsson +Fixed a bug which caused infinite loop if the mouse was clicked + +2003-06-24 Kristoffer Erlandsson +First working version +''' +usage='''pywmsysmon.py [options] +Available options are: +-h, --help print this help +-f, --barfgcolor set the foreground color of the memory bar +-g, --barbgcolor set the background color of the memory bar +-b, --background set the background color +-p, --graphforeground set the cpu graph foreground color +-a, --graphbackground set the cpu graph background color +-r, --rgbfile set the rgb file to get color codes from +-s, --procstat set the location of /proc/stat +-m, --procmeminfo set the location of /proc/meminfo +-i, --ignorenice ignore nice valued cpu usage +-u, --updatedelay delay (in seconds) between cpu graph updates +''' + +import sys +import time +import getopt +import os + +import pywmhelpers + +width = 64 +height = 64 + +xOffset = 4 +yOffset = 4 + +lettersStartX = 0 +lettersStartY = 74 +letterWidth = 6 +letterHeight = 8 + +digitsStartX = 0 +digitsStartY = 64 +digitWidth = 6 +digitHeight = 8 + +hGraphStartX = 7 +hGraphStartY = 53 +hGraphHeight = 4 +hGraphWidth = width - xOffset * 2 - 6 + +hGraphBgStartX = 72 +hGraphBgStartY = 53 + +hGraphLineStartX = 66 +hGraphLineStartY = 58 + +vGraphStartX = 7 +vGraphStartY = 7 +vGraphHeight = 43 +vGraphWidth = 50 + +vGraphLineStartX = 95 +vGraphLineStartY = 1 + +vGraphBgStartX = 97 +vGraphBgStartY = 1 + +letters = 'abcdefghijklmnopqrstuvwxyz' +digits = '0123456789:/-%. ' + +defaultConfigFile = '~/.pywmhdmonrc' +defaultRGBFiles = ('/usr/lib/X11/rgb.txt', '/usr/X11R6/lib/X11/rgb.txt') +defaultProcStat = '/proc/stat' +defaultProcMeminfo = '/proc/meminfo' + +class PywmSysMon: + def __init__(self, procMeminfo, procStat, ignoreNice=0, updateDelay=10): + self._procStat = procStat + self._procMeminfo = procMeminfo + self._ignoreNice = ignoreNice + + self._lastUsed = 0 + self._lastTotal = 0 + + self._usageHistory = [0.0] * vGraphWidth + + self._cpuUpdateDelay = updateDelay + self._memUpdateDelay = 30 + + def addUsageToHist(self, cpuUsage): + self._usageHistory = self._usageHistory[1:] + self._usageHistory.append(cpuUsage) + + def getMemInfo(self): + '''Get memory information. + + Return a tuple with (total_mem, used_mem, buffered_mem, cached_mem). + ''' + try: + meminfoFile = file(self._procMeminfo, 'r') + except IOError, e: + sys.stderr.write("Can't open meminfo file: %s.\n" % str(e)) + sys.exit(2) + theLine = None + for line in meminfoFile: + if line.startswith('Mem:'): + theLine = line + if theLine is None: + sys.stderr.write("Can't find memory information in %s.\n" % + self._procMeminfo) + sys.exit(4) + parts = [long(x) for x in theLine.split()[1:]] + total, used, free, shared, buffers, cached = parts + return (total, used, buffers, cached) + + def freeMem(self, memData): + '''Take a tuple as returned from getMemInfo and return the free mem. + ''' + total, used, buffers, cached = memData + reallyUsed = used - buffers - cached + free = total - reallyUsed + return free + + def getCPUUsage(self): + '''Get the current CPU usage. + + Only works for systems where this can be found in a /proc/stat like + file. Return the usage in percent. + ''' + try: + statFile = file(self._procStat, 'r') + except IOError, e: + sys.stderr.write("Can't open statfile: %s.\n" % str(e)) + sys.exit(2) + line = statFile.readline() + statFile.close() + cpu, nice, system, idle = [long(x) for x in line.split()[1:]] + used = cpu + system + if not self._ignoreNice: + used += nice + total = cpu + nice + system + idle + if total - self._lastTotal <= 0 or self._lastTotal == 0: + cpuUsage = 0.0 + else: + cpuUsage = 100.0 * (float(used - self._lastUsed) / float(total - + self._lastTotal)) + self._lastUsed = used + self._lastTotal = total + return cpuUsage + + def addString(self, s, x, y): + try: + pywmhelpers.addString(s, x, y, letterWidth, letterHeight, + lettersStartX, lettersStartY, letters, digitWidth, + digitHeight, digitsStartX, digitsStartY, digits, + xOffset, yOffset, width, height) + except ValueError, e: + sys.stderr.write('Error when painting string:\n' + str(e) + '\n') + sys.exit(3) + + def paintGraph(self, percentFilled, x, y, w): + '''Paint a graph with percentFilled percent filled. + + Paint at position x, y and with width w. + ''' + paintWidth = int(round(percentFilled/100.0 * w)) + if paintWidth > 0: + pywmhelpers.copyXPMArea( + hGraphLineStartX, hGraphLineStartY, paintWidth, hGraphHeight, + x, y) + if w - paintWidth > 0: + pywmhelpers.copyXPMArea( + hGraphBgStartX, hGraphBgStartY, w - paintWidth, hGraphHeight, + x + paintWidth, y) + + def drawVertLine(self, sourceX, sourceY, targX, targY, length): + '''Draw a vertical line. + ''' + if length > 0: + pywmhelpers.copyXPMArea(sourceX, sourceY, 1, length, targX, targY) + + def drawCPUUsageHistory(self): + '''Draw the complete CPU usage graph according to what's in the history. + ''' + count = 0 + for histItem in self._usageHistory: + lengthFilled = int(round(vGraphHeight * (histItem/100.0))) + lengthNotFilled = vGraphHeight - lengthFilled + self.drawVertLine(vGraphBgStartX, vGraphBgStartY, + vGraphStartX + count, vGraphStartY, + lengthNotFilled) + self.drawVertLine(vGraphLineStartX, vGraphLineStartY, + vGraphStartX + count, + vGraphStartY + lengthNotFilled, lengthFilled) + count += 1 + + def updateCPUInfo(self): + '''Update the current cpu usage graph.''' + currentUsage = self.getCPUUsage() + self.addUsageToHist(currentUsage) + self.drawCPUUsageHistory() + + def updateMemInfo(self): + '''Update the current memory usage graph.''' + memInfo = self.getMemInfo() + total = memInfo[0] + free = self.freeMem(memInfo) + percentUsed = 100.0 * (float(total - free) / float(total)) + self.paintGraph(percentUsed, hGraphStartX, hGraphStartY, hGraphWidth) + + def _checkForEvents(self): + event = pywmhelpers.getEvent() + while not event is None: + if event['type'] == 'destroynotify': + sys.exit(0) + event = pywmhelpers.getEvent() + + def mainLoop(self): + counter = -1 + while 1: + counter += 1 + self._checkForEvents() + if counter % self._cpuUpdateDelay == 0: + self.updateCPUInfo() + if counter % self._memUpdateDelay == 0: + self.updateMemInfo() + if counter == 999999: + counter = -1 + pywmhelpers.redraw() + time.sleep(0.1) + +def parseCommandLine(argv): + '''Parse the commandline. Return a dictionary with options and values.''' + shorts = 'hf:g:b:p:a:r:s:m:iu:' + longs = ['help=', 'barbgcolor=', 'barfgcolor=', 'background=', + 'graphforeground=', 'graphbackground=', 'rgbfile=', 'procstat=', + 'procmeminfo=', 'ignorenice', 'updatedelay='] + try: + opts, nonOptArgs = getopt.getopt(argv[1:], shorts, longs) + except getopt.GetoptError, e: + sys.stderr.write('Error when parsing commandline: ' + str(e) + '\n') + sys.stderr.write(usage) + sys.exit(2) + d = {} + for o, a in opts: + if o in ('-h', '--help'): + sys.stdout.write(usage) + sys.exit(0) + if o in ('-b', '--background'): + d['background'] = a + if o in ('-r', '--rgbfile'): + d['rgbfile'] = a + if o in ('-g', '--barbgcolor'): + d['barbgcolor'] = a + if o in ('-f', '--barfgcolor'): + d['barfgcolor'] = a + if o in ('-s', '--procstat'): + d['procstat'] = a + if o in ('-p', '--graphforeground'): + d['graphforeground'] = a + if o in ('-a', '--graphbackground'): + d['graphbackground'] = a + if o in ('-m', '--procmeminfo'): + d['procmeminfo'] = a + if o in ('-i', '--ignorenice'): + d['ignorenice'] = 1 + if o in ('-u', '--updatedelay'): + try: + d['updatedelay'] = int(a) * 10 + except ValueError: + sys.stderr.write( + "Value for updatedelay has to be an integer.\n") + sys.exit(2) + return d + +def parseColors(defaultRGBFileList, config, xpm): + rgbFileName = '' + for fn in defaultRGBFileList: + if os.access(fn, os.R_OK): + rgbFileName = fn + break + rgbFileName = config.get('rgbfile', rgbFileName) + useColors = 1 + if not os.access(rgbFileName, os.R_OK): + sys.stderr.write( + "Can't read the RGB file, try setting it differently using -r,\n") + sys.stderr.write( + "Ignoring your color settings, using the defaults.\n") + useColors = 0 + if useColors: + # Colors is a list with (, ) pairs. + colors = (('barfgcolor', 'graph'), + ('barbgcolor', 'graphbg'), + ('background', 'background'), + ('graphforeground', 'vgraph'), + ('graphbackground', 'vgraphbg')) + for key, value in colors: + col = config.get(key) + if not col is None: + code = pywmhelpers.getColorCode(col, rgbFileName) + if code is None: + sys.stderr.write('Bad colorcode for %s, ignoring.\n' % key) + else: + pywmhelpers.setColor(xpm, value, code) + + + +def main(): + config = parseCommandLine(sys.argv) + parseColors(defaultRGBFiles, config, xpm) + procStat = config.get('procstat', defaultProcStat) + if not os.access(procStat, os.R_OK): + sys.stderr.write( + "Can't read your procstat file, try setting it with -s.\n") + sys.exit(4) + procMeminfo = config.get('procmeminfo', defaultProcMeminfo) + if not os.access(procMeminfo, os.R_OK): + sys.stderr.write( + "Can't read your procmeminfo file, try setting it with -m.\n") + sys.exit(4) + ignoreNice = config.get('ignorenice', 0) + updateDelay = config.get('updatedelay', 30) + try: + programName = sys.argv[0].split(os.sep)[-1] + except IndexError: + programName = '' + sys.argv[0] = programName + pywmhelpers.setDefaultPixmap(xpm) + pywmhelpers.openXwindow(sys.argv, width, height) + pywmsysmon = PywmSysMon(procMeminfo, procStat, ignoreNice, updateDelay) + pywmsysmon.mainLoop() + + +xpm = \ +['160 100 15 1', + ' \tc #208120812081', + '.\tc #00000000FFFF', + 'o\tc #C71BC30BC71B', + 'O\tc #861782078E38', + '+\tc #EFBEF3CEEFBE', + '@\tc #618561856185', + '#\tc #9E79A2899E79', + '$\tc #410341034103', + 'o\tc #2020b2b2aaaa s indicator', + '/\tc #2020b2b2aaaa s graph', + '-\tc #707070707070 s graphbg', + '|\tc #2020b2b2aaaa s vgraph', + 'I\tc #707070707070 s vgraphbg', + 'X\tc #000000000000 s background', + '%\tc #2081B2CAAEBA s text', + ' ...............................................................................................', + ' .///..XXX..ooo..XXX..XXX......|.I..............................................................', + ' .///..XXX..ooo..XXX..XXX......|.I..............................................................', + ' .///..XXX..ooo..XXX..XXX......|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..XXX......|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..XXX......|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..XXX......|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..ooo..XXX......|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..ooo..XXX......|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..ooo..XXX......|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..XXX......|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..XXX......|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..XXX......|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..ooo......|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..ooo......|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..XXX..XXX..XXX..ooo......|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///..........................|.I..............................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...-------------------------------------------------------------------------------------...', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...-------------------------------------------------------------------------------------...', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...-------------------------------------------------------------------------------------...', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...-------------------------------------------------------------------------------------...', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///...........................................................................................', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///////////////////////////////////////////////////////////////////////////////////////////...', + ' XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX .///////////////////////////////////////////////////////////////////////////////////////////...', + ' .///////////////////////////////////////////////////////////////////////////////////////////...', + ' .///////////////////////////////////////////////////////////////////////////////////////////...', + ' ...............................................................................................', + ' ...............................................................................................', + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%%%%%XXX%XXX%%%%%X%%%%%X%XXX%X%%%%%X%%%%%X%%%%%X%%%%%X%%%%%XXXXXXXXXX%XXXXXXXXXX%X%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XX%%XXXXXXX%XXXXX%X%XXX%X%XXXXX%XXXXXXXXX%X%XXX%X%XXX%XX%%XXXXXX%XXXXXXXXXXXX%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XXX%XXXXXXX%XXXXX%X%XXX%X%XXXXX%XXXXXXXXX%X%XXX%X%XXX%XX%%XXXXX%%XXXXXXXXXXX%%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XXX%XXX%%%%%XX%%%%X%%%%%X%%%%%X%%%%%XXXXX%X%%%%%X%%%%%XXXXXXXXX%XXX%%%%%XXXX%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XXX%XXX%XXXXXXXXX%XXXXX%XXXXX%X%XXX%XXXXX%X%XXX%XXXXX%XXXXXXXX%%XXXXXXXXXXX%%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%XXX%XXX%XXX%XXXXXXXXX%XXXXX%XXXXX%X%XXX%XXXXX%X%XXX%XXXXX%XX%%XXXX%XXXXXXXXXXXX%XXXXX%%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'X%%%%%XX%%%XX%%%%%X%%%%%XXXXX%X%%%%%X%%%%%XXXXX%X%%%%%X%%%%%XX%%XXXX%XXXXXXXXXXXX%X%XXX%%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX............................', + '................................................................................................................................................................', + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', + 'XX%%%XX%%%%XXX%%%%X%%%%XX%%%%XX%%%%%X%%%%%X%XXX%XXX%XXXXXXX%X%XXX%X%XXXXX%XXX%X%%%%XX%%%%%X%%%%%X%%%%%X%%%%%X%%%%%X%%%%%X%XXX%X%XXX%X%XXX%X%XXX%X%XXX%X%%%%%XXXX', + 'X%XXX%X%XXX%X%XXXXX%XXX%X%XXXXX%XXXXX%XXXXX%XXX%XXX%XXXXXXX%X%XXX%X%XXXXX%%X%%X%XXX%X%XXX%X%XXX%X%XXX%X%XXX%X%XXXXXXX%XXX%XXX%X%XXX%X%XXX%X%XXX%X%XXX%XXXXX%XXXX', + 'X%XXX%X%XXX%X%XXXXX%XXX%X%XXXXX%XXXXX%XXXXX%XXX%XXX%XXXXXXX%X%XX%XX%XXXXX%X%X%X%XXX%X%XXX%X%XXX%X%XXX%X%XXX%X%XXXXXXX%XXX%XXX%X%XXX%X%XXX%XX%X%XX%XXX%XXXX%XXXXX', + 'X%%%%%X%%%%XX%XXXXX%XXX%X%%%%XX%%%%XX%X%%%X%%%%%XXX%XXXXXXX%X%%%XXX%XXXXX%XXX%X%XXX%X%XXX%X%%%%%X%%XX%X%%%%XX%%%%%XXX%XXX%XXX%X%XXX%X%XXX%XXX%XXX%%%%%XXX%XXXXXX', + 'X%XXX%X%XXX%X%XXXXX%XXX%X%XXXXX%XXXXX%XXX%X%XXX%XXX%XXXXXXX%X%XX%XX%XXXXX%XXX%X%XXX%X%XXX%X%XXXXX%X%X%X%XXX%XXXXX%XXX%XXX%XXX%X%XXX%X%X%X%XX%X%XXXXXX%XX%XXXXXXX', + 'X%XXX%X%XXX%X%XXXXX%XXX%X%XXXXX%XXXXX%XXX%X%XXX%XXX%XXX%XXX%X%XXX%X%XXXXX%XXX%X%XXX%X%XXX%X%XXXXX%XX%%X%XXX%XXXXX%XXX%XXX%XXX%X%XXX%X%%X%%X%XXX%XXXXX%X%XXXXXXXX', + 'X%XXX%X%%%%XXX%%%%X%%%%XX%%%%XX%XXXXX%%%%%X%XXX%XXX%XXXX%%%XX%XXX%X%%%%XX%XXX%X%XXX%X%%%%%X%XXXXX%%%%%X%XXX%X%%%%%XXX%XXXX%%%%XX%%%XX%XXX%X%XXX%X%%%%%X%%%%%XXXX', + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................', + '................................................................................................................................................................'] + + +if __name__ == '__main__': + main()