pywmdockapps/wmdocklib/pywmgeneral.c

820 lines
26 KiB
C
Raw Permalink Normal View History

2024-09-08 16:50:24 -06:00
/* 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:
*
2024-09-09 01:20:02 -06:00
* 2024-09-08 Fredrick W. Warren
* Ported from python 2 to python 3
*
2024-09-08 16:50:24 -06:00
* 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 <stdlib.h>
#include <stdio.h>
2024-09-09 01:20:02 -06:00
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "structmember.h"
2024-09-08 16:50:24 -06:00
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <stdarg.h>
#include <X11/Xlib.h>
#include <X11/xpm.h>
#include <X11/extensions/shape.h>
#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. */
2024-09-09 01:20:02 -06:00
if (!PyUnicode_Check(s)) {
2024-09-08 16:50:24 -06:00
PyErr_SetString(PyExc_TypeError, "String expected.");
return NULL;
}
2024-09-09 01:20:02 -06:00
// target[i] = strdup(PyUnicode_AsString(s));
target[i] = strdup(PyUnicode_AsUTF8(s));
2024-09-08 16:50:24 -06:00
}
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) {
/* sx - source x, sy - source y
* sw - width, sw - height
* dx - destination x, dy - destination y
*
* in the original wmgeneral.c variables are named differently.
*/
int sx, sy, sw, sh, dx, dy;
if (!PyArg_ParseTuple(args, "iiiiii", &sx, &sy, &sw, &sh, &dx, &dy))
return NULL;
copyXPMArea(sx, sy, sw, sh, 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);
switch(event.type) {
case Expose:
RedrawWindow();
break;
case EnterNotify:
case LeaveNotify:
/* needed by KeyPress/release, otherwise events go to parent. */
XSetInputFocus(display, PointerRoot, RevertToParent, CurrentTime);
break;
case KeyPress:
return Py_BuildValue("{s:s,s:i,s:i}",
"type", "keypress",
"state", event.xkey.state,
"keycode", event.xkey.keycode);
case ButtonPress:
case ButtonRelease:
return Py_BuildValue("{s:s,s:i,s:i,s:i}",
"type", event.type==ButtonPress?"buttonpress":"buttonrelease",
"button", event.xbutton.button,
"x", event.xbutton.x, "y", event.xbutton.y);
case ClientMessage:
if((Atom)event.xclient.data.l[0] == deleteAtom) {
XCloseDisplay(display);
return Py_BuildValue("{s:s}", "type", "destroynotify");
}
break;
case DestroyNotify:
/* This seems to never happen, why? */
XCloseDisplay(display);
return Py_BuildValue("{s:s}", "type", "destroynotify");
}
}
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}
};
typedef struct {
PyObject_HEAD
/* Type-specific fields go here. */
int has_drawable;
Pixmap drawable;
int width, height;
} drawable_DrawableObject;
static PyObject *
Drawable_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
drawable_DrawableObject *self;
self = (drawable_DrawableObject *)type->tp_alloc(type, 0);
if (self != NULL) {
self->has_drawable = 0;
self->width = 0;
self->height = 0;
}
return (PyObject *)self;
}
static int
Drawable_init(drawable_DrawableObject *self, PyObject *args, PyObject *kwds)
{
unsigned int w, h;
if (! PyArg_ParseTuple(args, "ii", &w, &h))
return -1;
if (!wmgen.attributes.depth) {
PyErr_SetString(PyExc_RuntimeError, "X client must be initialized first.");
return -1;
}
if (self->has_drawable)
XFreePixmap(display, self->drawable);
self->has_drawable = 1;
self->width = w;
self->height = h;
self->drawable = XCreatePixmap(display, wmgen.pixmap,
w, h, wmgen.attributes.depth);
return 0;
}
static void
Drawable_dealloc(drawable_DrawableObject *self)
{
if (self->has_drawable)
XFreePixmap(display, self->drawable);
}
static PyObject *
Drawable_xCopyAreaToWindow(drawable_DrawableObject *self, PyObject *args, PyObject *kwds)
{
unsigned int src_x, src_y, width, height, dst_x, dst_y;
if (! PyArg_ParseTuple(args, "iiiiii", &src_x, &src_y, &width, &height, &dst_x, &dst_y))
return NULL;
XCopyArea(display, self->drawable, wmgen.pixmap, NormalGC,
src_x, src_y, width, height, dst_x, dst_y);
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *
Drawable_xClear(drawable_DrawableObject *self, PyObject *args, PyObject *kwds)
{
XFillRectangle(display, self->drawable, NormalGC,
0, 0, self->width, self->height);
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *
Drawable_xCopyAreaFromWindow(drawable_DrawableObject *self, PyObject *args, PyObject *kwds)
{
unsigned int src_x, src_y, width, height, dst_x, dst_y;
if (! PyArg_ParseTuple(args, "iiiiii", &src_x, &src_y, &width, &height, &dst_x, &dst_y))
return NULL;
XCopyArea(display, wmgen.pixmap, self->drawable, NormalGC,
src_x, src_y, width, height, dst_x, dst_y);
Py_INCREF(Py_None);
return Py_None;
}
static PyMemberDef Drawable_members[] = {
{NULL} /* Sentinel */
};
static PyMethodDef Drawable_methods[] = {
{"xCopyAreaFromWindow", (PyCFunction)Drawable_xCopyAreaFromWindow, METH_VARARGS,
"copy from the drawable to the global pixmap"
},
{"xCopyAreaToWindow", (PyCFunction)Drawable_xCopyAreaToWindow, METH_VARARGS,
"copy from the global pixmap into the drawable"
},
{"xClear", (PyCFunction)Drawable_xClear, METH_NOARGS,
"clears the pixmap"
},
{NULL} /* Sentinel */
};
static PyTypeObject drawable_DrawableType = {
2024-09-09 01:20:02 -06:00
PyVarObject_HEAD_INIT(NULL, 0)
2024-09-08 16:50:24 -06:00
"pyywmgeneral.Drawable", /*tp_name*/
sizeof(drawable_DrawableObject), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)Drawable_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
"Drawable objects", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
Drawable_methods, /* tp_methods */
Drawable_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)Drawable_init, /* tp_init */
0, /* tp_alloc */
Drawable_new, /* tp_new */
};
/*****************************************************************************/
/* 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);
wmgen->attributes.depth = attributes.depth;
err = XpmCreatePixmapFromData(display, Root, pixmap_bytes, &(wmgen->pixmap),
&(wmgen->mask), &(wmgen->attributes));
if (err != XpmSuccess) {
fprintf(stderr, "Not enough free colorcells. %d\n", err);
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<MAX_MOUSE_REGION && !found; i++) {
if (mouse_region[i].enable &&
x <= mouse_region[i].right &&
x >= 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<sx*depth; j+=depth) {
bwrite >>= 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) {
/* in pywmgeneral_copyXPMArea variables are named differently.
*/
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,
ExposureMask |
ButtonPressMask |
ButtonReleaseMask | /* added ButtonReleaseMask *charkins*/
KeyPressMask | /* Try this to get keyboard working */
PointerMotionMask |
FocusChangeMask |
LeaveWindowMask |
StructureNotifyMask |
EnterWindowMask );
XSelectInput(display, iconwin,
ExposureMask |
ButtonPressMask |
ButtonReleaseMask | /* added ButtonReleaseMask *charkins*/
KeyPressMask | /* Try this to get keyboard working */
PointerMotionMask |
FocusChangeMask |
LeaveWindowMask |
StructureNotifyMask |
EnterWindowMask );
//ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask |
//ExposureMask |
//FocusChangeMask | EnterWindowMask |
//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);
}
}
2024-09-09 01:20:02 -06:00
static struct PyModuleDef pywmgeneral_module = {
PyModuleDef_HEAD_INIT, /* m_base */
"pywmgeneral", /* m_name */
NULL, /* m_doc */
-1, /* m_size */
PyWmgeneralMethods /* m_methods */
};
2024-09-08 16:50:24 -06:00
PyMODINIT_FUNC
2024-09-09 01:20:02 -06:00
PyInit_pywmgeneral(void)
{
2024-09-08 16:50:24 -06:00
PyObject* m;
drawable_DrawableType.tp_new = PyType_GenericNew;
if (PyType_Ready(&drawable_DrawableType) < 0)
2024-09-09 01:20:02 -06:00
return NULL;
2024-09-08 16:50:24 -06:00
2024-09-09 01:20:02 -06:00
m = PyModule_Create(&pywmgeneral_module);
2024-09-08 16:50:24 -06:00
if (m == NULL)
2024-09-09 01:20:02 -06:00
return NULL;
2024-09-08 16:50:24 -06:00
Py_INCREF(&drawable_DrawableType);
PyModule_AddObject(m, "Drawable", (PyObject *)&drawable_DrawableType);
2024-09-09 01:20:02 -06:00
return m;
2024-09-08 16:50:24 -06:00
}