From 5320092e59fb498f9343eecee7594c8dff2a2a6f Mon Sep 17 00:00:00 2001 From: mfrasca <> Date: Tue, 3 Oct 2006 08:03:55 +0000 Subject: [PATCH] 1569327 release 1.1 is broken cleaning up more things. MANIFEST.in includes ll readmes, wmdocklib correctly populated. --- MANIFEST | 8 +- MANIFEST.in | 2 +- examples/README | 97 ++++++ pywmgeneric/pywmgeneral.c | 596 +++++++++++++++++++++++++++++++++++++ pywmgeneric/pywmgeneral.h | 66 ++++ pywmgeneric/pywmhelpers.py | 253 ++++++++++++++++ setup.py | 3 +- 7 files changed, 1017 insertions(+), 8 deletions(-) create mode 100644 examples/README create mode 100644 pywmgeneric/pywmgeneral.c create mode 100644 pywmgeneric/pywmgeneral.h create mode 100644 pywmgeneric/pywmhelpers.py diff --git a/MANIFEST b/MANIFEST index 3c88525..712a846 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1,6 +1,8 @@ README.txt setup.py +examples/README examples/pywmdatetime.py +examples/pywmgeneric.py examples/pywmhdmon.py examples/pywmseti.py examples/pywmsysmon.py @@ -8,12 +10,8 @@ examples/sample.pywmdatetimerc examples/sample.pywmgenericrc examples/sample.pywmhdmonrc examples/sample.pywmsetirc -examples/setup.py -pywmgeneral/__init__.py -pywmgeneral/pywmhelpers.py -pywmgeneral/setup.py +wmdocklib/README wmdocklib/__init__.py wmdocklib/pywmgeneral.c wmdocklib/pywmgeneral.h -wmdocklib/pywmgeneric.py wmdocklib/pywmhelpers.py diff --git a/MANIFEST.in b/MANIFEST.in index 6c544c2..51163bd 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,2 @@ include *.txt -include */*.py */*.c */*.h */sample.pywm*rc +include */*.py */*.c */*.h */sample.pywm*rc */README diff --git a/examples/README b/examples/README new file mode 100644 index 0000000..efc5acd --- /dev/null +++ b/examples/README @@ -0,0 +1,97 @@ +[Pywmdatetime] +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. + +[Pywmgeneric] +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. + +[Pywmgeneric -- 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. + +[Pywmgeneric -- 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! + +[pywmhdmon] +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. + +[pywmhdmon -- BUGS] +The activity bar does not work with the proc filesystem that comes with +the 2.6 kernels. It is just a matter of rewriting the app to parse the +new format, but I'm low on time personally. + +[Pywmseti] +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. + +[pywmsysmon] +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. + diff --git a/pywmgeneric/pywmgeneral.c b/pywmgeneric/pywmgeneral.c new file mode 100644 index 0000000..58e2e2b --- /dev/null +++ b/pywmgeneric/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/pywmgeneric/pywmgeneral.h b/pywmgeneric/pywmgeneral.h new file mode 100644 index 0000000..f532437 --- /dev/null +++ b/pywmgeneric/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/pywmgeneric/pywmhelpers.py b/pywmgeneric/pywmhelpers.py new file mode 100644 index 0000000..bda6ad3 --- /dev/null +++ b/pywmgeneric/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/setup.py b/setup.py index 560d93a..284ae53 100644 --- a/setup.py +++ b/setup.py @@ -28,6 +28,5 @@ setup(name="pywmdockapps", scripts=['examples/pywmdatetime.py', 'examples/pywmhdmon.py', 'examples/pywmseti.py', - 'examples/pywmsysmon.py', - 'examples/setup.py'], + 'examples/pywmsysmon.py'], ext_modules = [module1])