#include #include #include /************************** DISPLAY PART ***************************/ typedef struct { BITMAPINFOHEADER bmiHeader; union { DWORD bmiMask[3]; short bmiIndices[255]; }; } screenbmp_t; typedef struct { PyObject_HEAD HWND win; int width, height, bpp; HDC dc; HPALETTE hpal, hprevpal; screenbmp_t screenbmpinfo; unsigned char* screenbmpdata; unsigned char* screenfirstline; int screenscanline; /* < 0 ! */ PyObject* keyevents; PyObject* mouseevents; PyObject* motionevent; } DisplayObject; #define DisplayObject_Check(v) ((v)->ob_type == &Display_Type) staticforward PyTypeObject Display_Type; static void flush(DisplayObject* self) { /*GdiFlush();*/ } static void release_window_data(DisplayObject* self) { if (self->hprevpal) { SelectPalette(self->dc, self->hprevpal, FALSE); self->hprevpal = (HPALETTE) NULL; } if (self->hpal) { DeleteObject(self->hpal); self->hpal = (HPALETTE) NULL; } if (self->dc && self->win) { ReleaseDC(self->win, self->dc); self->dc = (HDC) NULL; } } static void display_close(DisplayObject* self) { release_window_data(self); if (self->win) { SetWindowLong(self->win, 0, 0); DestroyWindow(self->win); self->win = (HWND) NULL; } } static LRESULT CALLBACK display_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { DisplayObject* self; switch (uMsg) { case WM_KEYDOWN: case WM_KEYUP: self = (DisplayObject*) GetWindowLong(hwnd, 0); if (self) { PyObject* v; int etype; if (self->keyevents == NULL) { self->keyevents = PyList_New(0); if (self->keyevents == NULL) break; } etype = (uMsg == WM_KEYDOWN) ? 2 : 3; v = Py_BuildValue("ii", (int) wParam, etype); if (v == NULL) break; PyList_Append(self->keyevents, v); Py_DECREF(v); } break; case WM_LBUTTONDOWN: self = (DisplayObject*) GetWindowLong(hwnd, 0); if (self) { PyObject* v; if (self->mouseevents == NULL) { self->mouseevents = PyList_New(0); if (self->mouseevents == NULL) break; } v = Py_BuildValue("ii", LOWORD(lParam), HIWORD(lParam)); if (v == NULL) break; PyList_Append(self->mouseevents, v); Py_DECREF(v); } break; case WM_MOUSEMOVE: self = (DisplayObject*) GetWindowLong(hwnd, 0); if (self) { Py_XDECREF(self->motionevent); self->motionevent = Py_BuildValue("ii", LOWORD(lParam), HIWORD(lParam)); } break; case WM_DESTROY: self = (DisplayObject*) GetWindowLong(hwnd, 0); if (self) { release_window_data(self); self->win = (HWND) NULL; } break; default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0; } static PyObject* new_display(PyObject* dummy, PyObject* args) { char* CLASSNAME = "winxshm"; WNDCLASS wcls; DisplayObject* self; int width, height, bytes, bpp, use_shm=0; if (!PyArg_ParseTuple(args, "ii|i", &width, &height, &use_shm)) return NULL; self = PyObject_New(DisplayObject, &Display_Type); if (self == NULL) return NULL; memset(&self->win, 0, ((char*)(self+1)) - ((char*)(&self->win))); self->width = width; self->height = height; /* set window class */ memset(&wcls, 0, sizeof(wcls)); wcls.style = CS_BYTEALIGNCLIENT; wcls.lpfnWndProc = &display_proc; wcls.cbWndExtra = sizeof(DisplayObject*); //wcls.hInstance = HINSTANCE; wcls.hCursor = LoadCursor(0, IDC_ARROW); wcls.lpszClassName = CLASSNAME; RegisterClass(&wcls); /* Create the window */ self->win = CreateWindowEx(0, CLASSNAME, NULL, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, width + 2*GetSystemMetrics(SM_CXFIXEDFRAME), height + 2*GetSystemMetrics(SM_CYFIXEDFRAME) + GetSystemMetrics(SM_CYCAPTION), (HWND) NULL, (HMENU) NULL, /*HINSTANCE*/ 0, (LPVOID) NULL); if (self->win == (HWND) NULL) goto err2; SetWindowLong(self->win, 0, (long) self); /* Create DC */ self->dc = GetDC(self->win); if (!self->dc) goto err2; self->bpp = bpp = GetDeviceCaps(self->dc, BITSPIXEL); if (bpp == 8) { struct { WORD palVersion; WORD palNumEntries; PALETTEENTRY palPalEntry[255]; } pal; pal.palNumEntries = GetSystemPaletteEntries(self->dc, 0, 255, pal.palPalEntry); if (pal.palNumEntries != 0) { int i; pal.palVersion = 0x300; self->hpal = CreatePalette((LOGPALETTE*)(&pal)); self->screenbmpinfo.bmiHeader.biClrUsed = pal.palNumEntries; self->hprevpal = SelectPalette(self->dc, self->hpal, FALSE); for (i=0; iscreenbmpinfo.bmiIndices[i] = i; } } } if (bpp != 15 && bpp != 16 && bpp != 24 && bpp != 32 && !self->hpal) { bpp = 24; /* default */ fprintf(stderr, "WARNING: a hi/true color screen mode of 15, 16, 24 or 32 bits per pixels\n"); fprintf(stderr, " is highly recommended !\n"); } /* Allocate screen bitmaps */ bytes = (bpp+7)/8; /* per pixel */ bytes = (bytes*width+3)&~3; /* per scan line */ self->screenscanline = -bytes; bytes = bytes*height; /* for the whole screen */ self->screenbmpdata = PyMem_Malloc(bytes); self->screenfirstline = self->screenbmpdata + bytes + self->screenscanline; if (self->screenbmpdata == NULL) { PyErr_NoMemory(); goto err2; } self->screenbmpinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); self->screenbmpinfo.bmiHeader.biWidth = self->width; self->screenbmpinfo.bmiHeader.biHeight = self->height; self->screenbmpinfo.bmiHeader.biPlanes = 1; self->screenbmpinfo.bmiHeader.biBitCount = (bpp+7)&~7; if (bpp == 16) { self->screenbmpinfo.bmiHeader.biCompression = BI_BITFIELDS; self->screenbmpinfo.bmiMask[0] = 0xF800; self->screenbmpinfo.bmiMask[1] = 0x07E0; self->screenbmpinfo.bmiMask[2] = 0x001F; } flush(self); return (PyObject*) self; err2: display_close(self); Py_DECREF(self); if (!PyErr_Occurred()) PyErr_SetString(PyExc_IOError, "cannot open window"); return NULL; } static void display_dealloc(DisplayObject* self) { display_close(self); Py_XDECREF(self->keyevents); Py_XDECREF(self->mouseevents); Py_XDECREF(self->motionevent); PyMem_Free(self->screenbmpdata); PyObject_Del(self); } static PyObject* display_close1(DisplayObject* self, PyObject* args) { display_close(self); Py_INCREF(Py_None); return Py_None; } static int checkopen(DisplayObject* self) { if (self->win) return 1; //PyErr_SetString(PyExc_IOError, "the window was closed"); PyErr_SetString(PyExc_SystemExit, "window closed."); return 0; } static PyObject* display_clear1(DisplayObject* self, PyObject* args) { if (!checkopen(self)) return NULL; memset(self->screenbmpdata, 0, (-self->screenscanline) * self->height); Py_INCREF(Py_None); return Py_None; } static void pack_pixel(DisplayObject* self, unsigned char *data, int r, int g, int b, int depth) { unsigned short pixel; switch( depth ) { case 8: data[0] = GetNearestPaletteIndex(self->hpal, (b<<16) | (g<<8) | r); break; case 15: pixel = ((r<<7) & 0x7c00) | ((g<<2) & 0x03e0) | ((b>>3) & 0x001f); data[0] = (pixel) & 0xff; data[1] = (pixel>>8) & 0xff; break; case 16: /* assumes 5,6,5 model. */ pixel = ((r<<8) & 0xf800) | ((g<<3) & 0x07e0) | ((b>>3) & 0x001f); data[0] = (pixel) & 0xff; data[1] = (pixel>>8) & 0xff; break; case 24: if( 1 ) { data[0] = b; data[1] = g; data[2] = r; break; } case 32: *((long *)data) = (r<<16) | (g<<8) | b; break; } } static PyObject* display_pixmap1(DisplayObject* self, PyObject* args) { int w,h; int length; unsigned char* input = NULL; long keycol = -1; if (!checkopen(self)) return NULL; if (!PyArg_ParseTuple(args, "ii|s#l", &w, &h, &input, &length, &keycol)) return NULL; if (1) /* SHM */ { int x, y; unsigned char *dst; int size, bytes_per_pixel; long packed_keycol = keycol; PyObject* result; PyObject* str; if (input == NULL ) { Py_INCREF(Py_None); return Py_None; } bytes_per_pixel = self->screenbmpinfo.bmiHeader.biBitCount/8; size = bytes_per_pixel*w*h; if( 3*w*h != length ) { PyErr_SetString(PyExc_TypeError, "bad string length"); return NULL; } /* Create a new string and fill it with the correctly packed image */ str = PyString_FromStringAndSize(NULL, size); if (!str) return NULL; if (keycol >= 0) switch( self->bpp ) { case 8: packed_keycol = 0xFF; break; case 15: packed_keycol = (1 << 10) | (1 << 5) | 1; break; case 16: packed_keycol = (1 << 11) | (1 << 5) | 1; break; default: packed_keycol = keycol; break; } result = Py_BuildValue("iiOl", w, h, str, packed_keycol); Py_DECREF(str); /* one ref left in 'result' */ if (!result) return NULL; dst = (unsigned char*) PyString_AS_STRING(str); memset(dst,0,size); for( y=0; ybpp); } return result; } } static PyObject* display_putppm1(DisplayObject* self, PyObject* args) { if (!checkopen(self)) return NULL; if (1) /* SHM */ { int x,y,w,h,scanline; int clipx=0, clipy=0, clipw=65536, cliph=65536; unsigned char* src; int length; long keycol; int bytes_per_pixel = self->screenbmpinfo.bmiHeader.biBitCount/8; unsigned char* data = self->screenfirstline; if (!PyArg_ParseTuple(args, "ii(iis#l)|(iiii)", &x, &y, &w, &h, &src, &length, &keycol, &clipx, &clipy, &clipw, &cliph) || !data) return NULL; scanline = bytes_per_pixel*w; if (scanline*h != length) { PyErr_SetString(PyExc_TypeError, "bad string length"); return NULL; } x -= clipx; y -= clipy; clipx += x; clipy += y; clipw += clipx; cliph += clipy; if (clipx<0) clipx=0; if (clipy<0) clipy=0; if (clipw>self->width) clipw=self->width; if (cliph>self->height) cliph=self->height; if (x clipw) w = clipw-x; if (y+h > cliph) h = cliph-y; data += bytes_per_pixel*x+y*self->screenscanline; while (h>0) { int i; int b; unsigned char* src0 = src; unsigned char* data0 = data; if (keycol < 0) for (i=0; iscreenscanline; h--; } } Py_INCREF(Py_None); return Py_None; } static PyObject* display_getppm1(DisplayObject* self, PyObject* args) { if (!checkopen(self)) return NULL; if (1) /* SHM */ { int x,y,w,h,scanline; int clipx=0, clipy=0, clipw=self->width, cliph=self->height; unsigned char* dst; int length; PyObject* ignored; PyObject* result; PyObject* str; int bytes_per_pixel = self->screenbmpinfo.bmiHeader.biBitCount/8; unsigned char* data = self->screenfirstline; if (!PyArg_ParseTuple(args, "(iiii)|O", &x, &y, &w, &h, &ignored) || !data) return NULL; scanline = bytes_per_pixel*w; length = scanline*h; str = PyString_FromStringAndSize(NULL, length); if (!str) return NULL; result = Py_BuildValue("iiOl", w, h, str, -1); Py_DECREF(str); /* one ref left in 'result' */ if (!result) return NULL; dst = (unsigned char*) PyString_AS_STRING(str); if (x clipw) w = clipw-x; if (y+h > cliph) h = cliph-y; data += bytes_per_pixel*x+y*self->screenscanline; while (h>0) { int i; int b; unsigned char* dst0 = dst; unsigned char* data0 = data; for (i=0; iscreenscanline; h--; } return result; } } static int readXevents(DisplayObject* self) { MSG Msg; while (PeekMessage(&Msg, (HWND) NULL, 0, 0, PM_REMOVE)) { DispatchMessage(&Msg); if (PyErr_Occurred()) return 0; } return checkopen(self); } #define ENABLE_EVENTS(mask) do { } while (0) /* nothing */ static PyObject* display_keyevents1(DisplayObject* self, PyObject* args) { PyObject* result; ENABLE_EVENTS(KeyPressMask|KeyReleaseMask); if (!readXevents(self)) return NULL; result = self->keyevents; if (result == NULL) result = PyList_New(0); else self->keyevents = NULL; return result; } static PyObject* display_mouseevents1(DisplayObject* self, PyObject* args) { PyObject* result; ENABLE_EVENTS(ButtonPressMask); result = self->mouseevents; if (result == NULL) result = PyList_New(0); else self->mouseevents = NULL; return result; } static PyObject* display_pointermotion1(DisplayObject* self, PyObject* args) { PyObject* result; ENABLE_EVENTS(PointerMotionMask); result = self->motionevent; if (result == NULL) { Py_INCREF(Py_None); result = Py_None; } else self->motionevent = NULL; return result; } static PyObject* display_flip1(DisplayObject* self, PyObject* args) { int r; if (!checkopen(self)) return NULL; if (self->hpal) RealizePalette(self->dc); r = SetDIBitsToDevice(self->dc, 0, 0, self->width, self->height, 0, 0, 0, self->height, self->screenbmpdata, (BITMAPINFO*)(&self->screenbmpinfo), DIB_PAL_COLORS); if (!r) { PyErr_SetString(PyExc_IOError, "SetDIBitsToDevice failed"); return NULL; } flush(self); if (!readXevents(self)) return NULL; Py_INCREF(Py_None); return Py_None; } static PyObject* display_shmmode(DisplayObject* self, PyObject *args) { return PyInt_FromLong(0); } static PyObject* display_settitle(DisplayObject* self, PyObject* args) { char* title; if (!checkopen(self)) return NULL; if (!PyArg_ParseTuple(args, "s", &title)) return NULL; SetWindowText(self->win, title); Py_INCREF(Py_None); return Py_None; } static PyMethodDef display_methods[] = { {"close", (PyCFunction)display_close1, METH_VARARGS, NULL}, {"flip", (PyCFunction)display_flip1, METH_VARARGS, NULL}, {"clear", (PyCFunction)display_clear1, METH_VARARGS, NULL}, {"pixmap", (PyCFunction)display_pixmap1, METH_VARARGS, NULL}, {"putppm", (PyCFunction)display_putppm1, METH_VARARGS, NULL}, {"getppm", (PyCFunction)display_getppm1, METH_VARARGS, NULL}, {"keyevents",(PyCFunction)display_keyevents1,METH_VARARGS, NULL}, {"mouseevents",(PyCFunction)display_mouseevents1,METH_VARARGS,NULL}, {"pointermotion",(PyCFunction)display_pointermotion1,METH_VARARGS,NULL}, {"shmmode", (PyCFunction)display_shmmode, METH_VARARGS, NULL}, {"settitle", (PyCFunction)display_settitle, METH_VARARGS, NULL}, {NULL, NULL} /* sentinel */ }; static PyObject* display_getattr(DisplayObject* self, char* name) { return Py_FindMethod(display_methods, (PyObject*)self, name); } statichere PyTypeObject Display_Type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "Display", /*tp_name*/ sizeof(DisplayObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ (destructor)display_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ (getattrfunc)display_getattr, /*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*/ }; /************************** AUDIO PART ***************************/ #define NUM_WAVE_HDR 2 typedef struct { PyObject_HEAD HWAVEOUT waveOut; HANDLE doneEvent; WAVEHDR waveHdr[NUM_WAVE_HDR]; int waveHdrCount, waveHdrNext; } AudioObject; #define AudioObject_Check(v) ((v)->ob_type == &Audio_Type) staticforward PyTypeObject Audio_Type; static PyObject* new_audio(PyObject* dummy, PyObject* args) { WAVEFORMATEX wf; int channels, freq, bits, err, bufsize; AudioObject* self; if (!PyArg_ParseTuple(args, "iiii", &channels, &freq, &bits, &bufsize)) return NULL; self = PyObject_New(AudioObject, &Audio_Type); if (self == NULL) return NULL; self->waveHdrCount = 0; self->waveHdrNext = 0; self->waveOut = 0; self->doneEvent = CreateEvent(NULL, FALSE, TRUE, NULL); memset(&wf, 0, sizeof(wf)); wf.wFormatTag = WAVE_FORMAT_PCM; wf.nChannels = channels; wf.nSamplesPerSec = freq; wf.wBitsPerSample = bits; wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8; wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; err = waveOutOpen(&self->waveOut, WAVE_MAPPER, &wf, (DWORD) self->doneEvent, 0, CALLBACK_EVENT); if (err != MMSYSERR_NOERROR || self->doneEvent == NULL) { Py_DECREF(self); PyErr_SetString(PyExc_IOError, "cannot open audio device"); return NULL; } while (self->waveHdrCount < NUM_WAVE_HDR) { WAVEHDR* wh = &self->waveHdr[self->waveHdrCount]; wh->lpData = PyMem_Malloc(bufsize); wh->dwBufferLength = bufsize; wh->dwFlags = 0; if (wh->lpData == NULL || waveOutPrepareHeader(self->waveOut, wh, sizeof(WAVEHDR)) != MMSYSERR_NOERROR) { Py_DECREF(self); return NULL; } wh->dwFlags |= WHDR_DONE; self->waveHdrCount++; } return (PyObject*) self; } static void audio_close(AudioObject* self) { if (self->waveOut != 0) { waveOutReset(self->waveOut); while (self->waveHdrCount > 0) { WAVEHDR* wh = &self->waveHdr[--self->waveHdrCount]; waveOutUnprepareHeader(self->waveOut, wh, sizeof(WAVEHDR)); PyMem_Free(wh->lpData); } waveOutClose(self->waveOut); self->waveOut = 0; } } static void audio_dealloc(AudioObject* self) { audio_close(self); PyObject_Del(self); } static PyObject* audio_wait1(AudioObject* self, PyObject* args) { float delay = -1.0; if (!PyArg_ParseTuple(args, "|f", &delay)) return NULL; if (self->waveHdrNext >= self->waveHdrCount) { PyErr_SetString(PyExc_IOError, "audio device not ready"); return NULL; } Py_BEGIN_ALLOW_THREADS while (!(self->waveHdr[self->waveHdrNext].dwFlags & WHDR_DONE)) { WaitForSingleObject(self->doneEvent, delay<0.0?INFINITE:(DWORD)(delay*1000.0)); } Py_END_ALLOW_THREADS Py_INCREF(Py_None); return Py_None; } static PyObject* audio_ready1(AudioObject* self, PyObject* args) { if (!PyArg_ParseTuple(args, "")) return NULL; return PyInt_FromLong(self->waveHdrNext < self->waveHdrCount && (self->waveHdr[self->waveHdrNext].dwFlags & WHDR_DONE)); } static PyObject* audio_write1(AudioObject* self, PyObject* args) { WAVEHDR* wh; char* buffer; int bufsize; if (!PyArg_ParseTuple(args, "s#", &buffer, &bufsize)) return NULL; if (self->waveHdrNext >= self->waveHdrCount) { PyErr_SetString(PyExc_IOError, "audio device not ready"); return NULL; } wh = &self->waveHdr[self->waveHdrNext]; if (!(wh->dwFlags & WHDR_DONE)) { PyErr_SetString(PyExc_IOError, "audio device would block"); return NULL; } if ((DWORD) bufsize != wh->dwBufferLength) { PyErr_SetString(PyExc_ValueError, "bufsize mismatch"); return NULL; } wh->dwFlags &= ~WHDR_DONE; memcpy(wh->lpData, buffer, bufsize); if (waveOutWrite(self->waveOut, wh, sizeof(WAVEHDR)) != MMSYSERR_NOERROR) { PyErr_SetString(PyExc_IOError, "audio device write error"); return NULL; } self->waveHdrNext++; if (self->waveHdrNext >= self->waveHdrCount) self->waveHdrNext = 0; Py_INCREF(Py_None); return Py_None; } static PyObject* audio_close1(AudioObject* self, PyObject* args) { if (!PyArg_ParseTuple(args, "")) return NULL; audio_close(self); Py_INCREF(Py_None); return Py_None; } static PyMethodDef audio_methods[] = { {"ready", (PyCFunction)audio_ready1, METH_VARARGS, NULL}, {"wait", (PyCFunction)audio_wait1, METH_VARARGS, NULL}, {"write", (PyCFunction)audio_write1, METH_VARARGS, NULL}, {"close", (PyCFunction)audio_close1, METH_VARARGS, NULL}, {NULL, NULL} /* sentinel */ }; static PyObject* audio_getattr(AudioObject* self, char* name) { return Py_FindMethod(audio_methods, (PyObject*)self, name); } statichere PyTypeObject Audio_Type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "Audio", /*tp_name*/ sizeof(AudioObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ (destructor)audio_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ (getattrfunc)audio_getattr, /*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*/ }; static PyMethodDef WinMethods[] = { {"Display", new_display, METH_VARARGS}, {"Audio", new_audio, METH_VARARGS}, {NULL, NULL} /* Sentinel */ }; void initwingame(void) { Display_Type.ob_type = &PyType_Type; Audio_Type.ob_type = &PyType_Type; Py_InitModule("wingame", WinMethods); }