summaryrefslogtreecommitdiff
path: root/display
diff options
context:
space:
mode:
authorDiego Roversi <diegor@tiscali.it>2019-09-08 18:12:27 +0200
committerDiego Roversi <diegor@tiscali.it>2019-09-08 18:12:27 +0200
commit1d9925c287b318ec21343e2682b51ab6a36ae8db (patch)
tree17d1c0ac21eea6f291146520afa8381db4586fb4 /display
initial commit from cvs 1.6.2
Diffstat (limited to 'display')
-rw-r--r--display/.cvsignore4
-rw-r--r--display/Client.py170
-rw-r--r--display/Makefile2
-rw-r--r--display/__init__.py1
-rw-r--r--display/caching.py261
-rw-r--r--display/dpy_gtk.py204
-rw-r--r--display/dpy_pygame.py245
-rwxr-xr-xdisplay/dpy_windows.py23
-rw-r--r--display/dpy_x.py40
-rw-r--r--display/modes.py196
-rw-r--r--display/music1.py33
-rw-r--r--display/pclient.py860
-rw-r--r--display/playback.py203
-rw-r--r--display/puremixer.py123
-rw-r--r--display/pythonxlibintf.py202
-rwxr-xr-xdisplay/setup.py16
-rw-r--r--display/snd_linux.py148
-rw-r--r--display/snd_off.py5
-rw-r--r--display/snd_pygame.py82
-rw-r--r--display/snd_windows.py93
-rw-r--r--display/windows/.cvsignore3
-rwxr-xr-xdisplay/windows/wingame.c867
-rwxr-xr-xdisplay/windows/wingame.def2
-rwxr-xr-xdisplay/windows/wingame.dsp111
-rwxr-xr-xdisplay/windows/wingame.dsw29
-rw-r--r--display/xshm.c1202
-rwxr-xr-xdisplay/xshm.sobin0 -> 112912 bytes
27 files changed, 5125 insertions, 0 deletions
diff --git a/display/.cvsignore b/display/.cvsignore
new file mode 100644
index 0000000..687980a
--- /dev/null
+++ b/display/.cvsignore
@@ -0,0 +1,4 @@
+*.py[co]
+build
+*.so
+*.pyd
diff --git a/display/Client.py b/display/Client.py
new file mode 100644
index 0000000..3d1330d
--- /dev/null
+++ b/display/Client.py
@@ -0,0 +1,170 @@
+#! /usr/bin/env python
+
+# __________
+import os, sys
+if __name__ == '__main__':
+ LOCALDIR = sys.argv[0]
+else:
+ LOCALDIR = __file__
+try:
+ LOCALDIR = os.readlink(LOCALDIR)
+except:
+ pass
+LOCALDIR = os.path.dirname(os.path.abspath(LOCALDIR))
+# ----------
+
+sys.path.insert(0, os.path.dirname(LOCALDIR))
+sys.path.insert(0, LOCALDIR)
+import common
+import pclient
+import modes
+
+
+UdpLookForServer = [
+ '127.0.0.1',
+ '<broadcast>',
+ ]
+
+def parse_cmdline(argv):
+ # parse command-line
+ def usage():
+ print >> sys.stderr, 'usage:'
+ print >> sys.stderr, ' python Client.py [-d#] [-s#] [extra options] [host[:port]]'
+ print >> sys.stderr
+ print >> sys.stderr, 'options:'
+ print >> sys.stderr, ' host search for a game on the given machine'
+ print >> sys.stderr, ' host:port connect to the given game server'
+ print >> sys.stderr, ' (default search for any local server)'
+ print >> sys.stderr, ' -d# --display=# graphic driver (see below)'
+ print >> sys.stderr, ' -s# --sound=# sound driver (see below)'
+ print >> sys.stderr, ' --music=no disable background music'
+ print >> sys.stderr, ' -h --help display this text'
+ print >> sys.stderr, ' -m --metaserver connect with the help of the metaserver'
+ print >> sys.stderr, ' (list servers with Client.py -m)'
+ print >> sys.stderr, ' -t --tcp for slow or proxy connections'
+ print >> sys.stderr, ' -u --udp for fast direct connections'
+ print >> sys.stderr, ' (default is to autodetect tcp or udp)'
+ print >> sys.stderr, ' --port UDP=# or #:# fixed inbound udp port or host:port'
+ print >> sys.stderr, ' --port TCP=# fixed inbound tcp port (-m only)'
+ print >> sys.stderr
+ print >> sys.stderr, 'graphic drivers:'
+ for info in modes.graphicmodeslist():
+ info.printline(sys.stderr)
+ print >> sys.stderr
+ print >> sys.stderr, 'sound drivers:'
+ for info in modes.soundmodeslist():
+ info.printline(sys.stderr)
+ print >> sys.stderr
+ sys.exit(2)
+
+ shortopts = 'd:s:htum'
+ longopts = ['display=', 'sound=', 'music=', 'help', 'tcp', 'udp',
+ 'cfg=', 'metaserver', 'port=']
+ for info in modes.graphicmodeslist() + modes.soundmodeslist():
+ short, long = info.getformaloptions()
+ shortopts += short
+ longopts += long
+ try:
+ from getopt import gnu_getopt as getopt
+ except ImportError:
+ from getopt import getopt
+ from getopt import error
+ try:
+ opts, args = getopt(argv, shortopts, longopts)
+ except error, e:
+ print >> sys.stderr, 'Client.py: %s' % str(e)
+ print >> sys.stderr
+ usage()
+
+ metaserver = 0
+ driver = sound = None
+ extraopts = {}
+ for key, value in opts:
+ if key in ('-d', '--display'):
+ driver = value
+ elif key in ('-s', '--sound'):
+ sound = value
+ elif key in ('-t', '--tcp'):
+ extraopts['udp_over_tcp'] = 1
+ elif key in ('-u', '--udp'):
+ extraopts['udp_over_tcp'] = 0
+ elif key in ('-m', '--metaserver'):
+ metaserver = 1
+ elif key == '--port':
+ import common.msgstruct
+ try:
+ portname, value = value.split('=')
+ if portname == 'UDP':
+ portname = 'CLIENT'
+ elif portname == 'TCP':
+ portname = 'BACK'
+ except ValueError:
+ portname = 'CLIENT'
+ if portname == 'CLIENT' and ':' in value:
+ udphostname, value = value.split(':')
+ common.msgstruct.PORTS['sendudpto'] = udphostname
+ common.msgstruct.PORTS[portname] = int(value)
+ elif key == '--cfg':
+ extraopts['cfgfile'] = value
+ elif key in ('-h', '--help'):
+ usage()
+ else:
+ extraopts[key] = value
+ mode = driver, sound, extraopts
+
+ if metaserver:
+ if len(args) == 0:
+ metalist()
+ sys.exit(0)
+ elif len(args) != 1 or ':' not in args[0]:
+ usage()
+ return metaconnect(args[0]), mode
+
+ if args:
+ if len(args) > 1:
+ usage()
+ hosts = args[0].split(':')
+ if len(hosts) == 1:
+ host, = hosts
+ from common import hostchooser
+ server = hostchooser.pick([host] * 5)
+ elif len(hosts) == 2:
+ host, port = hosts
+ try:
+ port = int(port)
+ except ValueError:
+ usage()
+ server = host, port
+ else:
+ usage()
+ return directconnect(server), mode
+
+ from common import hostchooser
+ server = hostchooser.pick(UdpLookForServer * 3)
+ return directconnect(server), mode
+
+def directconnect(sockaddr):
+ print "connecting to %s:%d..." % sockaddr
+ from socket import socket, AF_INET, SOCK_STREAM
+ s = socket(AF_INET, SOCK_STREAM)
+ s.connect(sockaddr)
+ return s, sockaddr
+
+def metaconnect(metaaddr):
+ from metaserver import metaclient
+ import common.msgstruct
+ port = common.msgstruct.PORTS.get('BACK')
+ s = metaclient.meta_connect(metaaddr, port)
+ sockaddr = s.getpeername()
+ return s, sockaddr
+
+def metalist():
+ from metaserver import metaclient
+ metaclient.print_server_list()
+
+def main():
+ (s, sockaddr), mode = parse_cmdline(sys.argv[1:])
+ pclient.run(s, sockaddr, mode)
+
+if __name__ == '__main__':
+ main()
diff --git a/display/Makefile b/display/Makefile
new file mode 100644
index 0000000..214347a
--- /dev/null
+++ b/display/Makefile
@@ -0,0 +1,2 @@
+xshm.so: xshm.c
+ python setup.py build_ext -i
diff --git a/display/__init__.py b/display/__init__.py
new file mode 100644
index 0000000..ea30561
--- /dev/null
+++ b/display/__init__.py
@@ -0,0 +1 @@
+#empty
diff --git a/display/caching.py b/display/caching.py
new file mode 100644
index 0000000..27d772c
--- /dev/null
+++ b/display/caching.py
@@ -0,0 +1,261 @@
+from __future__ import generators
+import os, md5, sys
+#import common.debug
+
+
+class FileCache:
+ MAX_FILES = 8
+
+ def __init__(self):
+ self.cache = {}
+ self.time = 0
+
+ def access(self, filename, position, writing=0):
+ if filename in self.cache:
+ time, mode, f = self.cache[filename]
+ if writing > mode:
+ f.close()
+ del self.cache[filename]
+ if filename not in self.cache:
+ if len(self.cache) >= FileCache.MAX_FILES:
+ (time, mode, f), k = min([(v,k) for (k,v) in self.cache.items()])
+ f.close()
+ del self.cache[k]
+ try:
+ f = open(filename, ('rb', 'r+b')[writing])
+ except (IOError, OSError):
+ if not writing:
+ raise
+ if not os.path.isdir(os.path.dirname(filename)):
+ os.mkdir(os.path.dirname(filename))
+ f = open(filename, 'w+b')
+ mode = writing
+ self.time += 1
+ self.cache[filename] = self.time, mode, f
+ f.seek(position)
+ return f
+
+
+class MemoryBlock:
+ def __init__(self, data):
+ self.data = data
+ def overwrite(self, newdata):
+ self.data = newdata
+ def read(self):
+ return self.data
+
+class FileBlock:
+ def __init__(self, filename, position, length, readonly=1, complete=1):
+ self.filename = filename
+ self.position = position
+ self.length = length
+ self.readonly = readonly
+ self.complete = complete
+ def overwrite(self, newdata):
+ self.memorydata = newdata
+ if self.readonly:
+ print >> sys.stderr, "cannot overwrite file", self.filename
+ return
+ try:
+ f = Data.Cache.access(self.filename, self.position, writing=1)
+ f.write(newdata)
+ except (IOError, OSError):
+ print >> sys.stderr, "cache write error:", self.filename
+ return
+ self.complete = 1
+ del self.memorydata
+ def read(self):
+ if self.complete:
+ f = Data.Cache.access(self.filename, self.position)
+ return f.read(self.length)
+ else:
+ return self.memorydata
+
+
+class Data:
+ SafeChars = {}
+ for c in ".abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789":
+ SafeChars[c] = c
+ Translate = ''.join([SafeChars.get(chr(c), '_') for c in range(256)])
+ del c, SafeChars
+ Cache = FileCache()
+
+ def __init__(self):
+ self.content = {}
+ self.backupfile = None
+ self.readonly = 0
+
+ clear = __init__
+
+ ### Public interface ###
+
+ def store(self, position, data, filename=None, readonly=1):
+ """This class assumes that all accesses to block within the data
+ are done for disjoint intervals: no overlapping writes !"""
+ if self.content is not None:
+ try:
+ self.content[position].overwrite(data)
+ except KeyError:
+ if filename is None:
+ self.content[position] = MemoryBlock(data)
+ else:
+ self.content[position] = FileBlock(filename, position,
+ len(data), readonly)
+ if self.backupfile and not self.readonly:
+ try:
+ f = Data.Cache.access(self.backupfile, position, writing=1)
+ f.write(data)
+ f.flush()
+ except (IOError, OSError):
+ print >> sys.stderr, "cache write error:", self.backupfile
+
+ def loadfrom(self, filename, position, length, checksum):
+ """Try to load data from the given filename, with the given
+ expected MD5 checksum. The filename must be Unix-style, and is
+ looked up both in the directory SOURCEDIR and with a mangled name
+ in the cache directory CACHEDIR."""
+ directname = os.path.join(self.SOURCEDIR, *filename.split('/'))
+ mangledname = filename.translate(Data.Translate)
+ cachename = os.path.join(self.CACHEDIR, mangledname)
+ for name, readonly in ((directname, 1), (cachename, 0)):
+ try:
+ f = Data.Cache.access(name, position)
+ data = f.read(length)
+ except (IOError, OSError):
+ pass
+ else:
+ if len(data) == length and md5.new(data).digest() == checksum:
+ # correct data
+ self.store(position, data, name, readonly)
+ return 1
+ if self.content is not None and not self.content.has_key(position):
+ self.content[position] = FileBlock(cachename, position, length,
+ readonly=0, complete=0)
+ elif self.readonly:
+ print >> sys.stderr, "Note: the music data has changed. You can get"
+ print >> sys.stderr, "the server's version by deleting", directname
+ return 1 # incorrect data, but ignored
+ return 0
+
+ def read(self):
+ """Return the data as built so far."""
+ if self.content is not None:
+ items = self.content.items()
+ items.sort()
+ result = ''
+ for position, block in items:
+ if len(result) < position:
+ result += '\x00' * (position-len(result))
+ data = block.read()
+ result = result[:position] + data + result[position+len(data):]
+ return result
+ else:
+ f = Data.Cache.access(self.backupfile, 0)
+ return f.read()
+
+ def fopen(self):
+ if self.content is not None:
+ from cStringIO import StringIO
+ return StringIO(self.read())
+ else:
+ return Data.Cache.access(self.backupfile, 0)
+
+ def freezefilename(self, fileexthint='.wav'):
+ """Return the name of a file from which the data can be read. If all
+ the current data comes from the same file, it is assumed to be exactly
+ the file that we want."""
+ if not self.backupfile:
+ files = {}
+ for position, block in self.content.items():
+ if not isinstance(block, FileBlock):
+ break
+ if block.complete:
+ files[block.filename] = block
+ else:
+ if len(files) == 1:
+ self.backupfile, block = files.items()[0]
+ self.readonly = block.readonly
+ if not self.backupfile:
+ self.backupfile = mktemp(fileexthint)
+ f = Data.Cache.access(self.backupfile, 0, writing=1)
+ for position, block in self.content.items():
+ f.seek(position)
+ f.write(block.read())
+ f.flush()
+ #print 'freezefilename ->', self.backupfile
+ #print ' readonly =', self.readonly
+ self.content = None
+ return self.backupfile
+
+# ____________________________________________________________
+# Temporary files management
+# from the 'py' lib, mostly written by hpk
+
+def try_remove_dir(udir):
+ try:
+ for name in os.listdir(udir):
+ try:
+ os.unlink(os.path.join(udir, name))
+ except:
+ pass
+ os.rmdir(udir)
+ except:
+ pass
+
+def make_numbered_dir(prefix='tmp-bub-n-bros-', rootdir=None, keep=0,
+ lock_timeout = 172800): # two days
+ """ return unique directory with a number greater than the current
+ maximum one. The number is assumed to start directly after prefix.
+ Directories with a number less than (maxnum-keep) will be removed.
+ """
+ import atexit, tempfile
+ if rootdir is None:
+ rootdir = tempfile.gettempdir()
+
+ def parse_num(bn):
+ """ parse the number out of a path (if it matches the prefix) """
+ if bn.startswith(prefix):
+ try:
+ return int(bn[len(prefix):])
+ except ValueError:
+ pass
+
+ # compute the maximum number currently in use with the
+ # prefix
+ maxnum = -1
+ for basename in os.listdir(rootdir):
+ num = parse_num(basename)
+ if num is not None:
+ maxnum = max(maxnum, num)
+
+ # make the new directory
+ udir = os.path.join(rootdir, prefix + str(maxnum+1))
+ os.mkdir(udir)
+
+ # try to remove the directory at process exit
+ atexit.register(try_remove_dir, udir)
+
+ # prune old directories
+ for basename in os.listdir(rootdir):
+ num = parse_num(basename)
+ if num is not None and num <= (maxnum - keep):
+ d1 = os.path.join(rootdir, basename)
+ try:
+ t1 = os.stat(d1).st_mtime
+ t2 = os.stat(udir).st_mtime
+ if abs(t2-t1) < lock_timeout:
+ continue # skip directories still recently used
+ except:
+ pass
+ try_remove_dir(d1)
+ return udir
+
+def enumtempfiles():
+ tempdir = make_numbered_dir()
+ i = 0
+ while True:
+ yield os.path.join(tempdir, 'b%d' % i)
+ i += 1
+
+def mktemp(fileext, gen = enumtempfiles()):
+ return gen.next() + fileext
diff --git a/display/dpy_gtk.py b/display/dpy_gtk.py
new file mode 100644
index 0000000..7b73516
--- /dev/null
+++ b/display/dpy_gtk.py
@@ -0,0 +1,204 @@
+
+ ################################################
+## GTK-based implementation of xshm ##
+################################################
+
+import os, sys, math
+from modes import KeyPressed, KeyReleased
+import caching
+
+def import_trickery():
+ global gtk, gdk
+ argv = sys.argv[:]
+ del sys.argv[1:]
+ import gtk
+ from gtk import gdk
+ sys.argv[:] = argv
+import_trickery()
+
+
+class Display:
+
+ def __init__(self, width, height, title, zoom="100"):
+ if zoom.endswith('%'):
+ zoom = zoom[:-1]
+ scale = float(zoom) / 100.0
+ iscale = int(scale+0.001)
+ if abs(scale - iscale) < 0.002:
+ scale = iscale
+ self.scale = scale
+
+ self.width = int(width * scale)
+ self.height = int(height * scale)
+ self.tempppmfile = caching.mktemp('.ppm')
+
+ # create a top level window
+ w = self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+ w.connect("destroy", lambda w: sys.exit())
+ w.connect("key-press-event", self.key_press_event)
+ w.connect("key-release-event", self.key_release_event)
+ w.connect("motion-notify-event", self.motion_notify_event)
+ w.connect("button-press-event", self.button_press_event)
+ w.add_events(gdk.KEY_PRESS_MASK |
+ gdk.POINTER_MOTION_MASK |
+ gdk.BUTTON_PRESS_MASK)
+ w.resize(self.width, self.height)
+ w.set_title(title)
+ w.show()
+
+ self.offscreen = gtk.create_pixmap(w.window, self.width, self.height)
+ self.gc = gdk.gc_new(w.window)
+ self.gc.set_rgb_fg_color(gdk.color_parse('#000000'))
+
+ self.events_key = []
+ self.events_mouse = []
+ self.event_motion = None
+
+ pixel = "\x00\x00\x80"
+ hole = "\x01\x01\x01"
+ pb = self.pixmap(32, 32, ((pixel+hole)*16 + (hole+pixel)*16) * 16, 0x010101)
+ self.taskbkgnd = self.renderpixbuf(pb)
+
+ def taskbar(self, (x, y, w, h)):
+ scale = self.scale
+ x2 = x+w
+ y2 = y+h
+ x, y, x2, y2 = int(x*scale), int(y*scale), int(x2*scale), int(y2*scale)
+ pixmap, gc, ignored = self.taskbkgnd
+ for j in range(y, y2, 32):
+ for i in range(x, x2, 32):
+ gc.set_clip_origin(i, j)
+ self.offscreen.draw_drawable(gc, pixmap, 0, 0,
+ i, j, x2-i, y2-j)
+
+ def pixmap(self, w, h, data, colorkey=-1):
+ filename = self.tempppmfile
+ f = open(filename, 'wb')
+ print >> f, 'P6'
+ print >> f, w, h
+ print >> f, 255
+ f.write(data)
+ f.close()
+ pb = gdk.pixbuf_new_from_file(filename)
+ if colorkey >= 0:
+ pb = pb.add_alpha(1, chr(colorkey >> 16),
+ chr((colorkey >> 8) & 0xFF),
+ chr(colorkey & 0xFF))
+ if self.scale == 1:
+ return self.renderpixbuf((pb,))
+ else:
+ return (pb,)
+
+ def renderpixbuf(self, input):
+ if len(input) == 3:
+ return input
+ pb, = input
+ pixmap, mask = pb.render_pixmap_and_mask()
+ if mask is not None:
+ gc = gdk.gc_new(self.window.window)
+ gc.set_clip_mask(mask)
+ return (pixmap, gc, mask)
+ else:
+ return (pixmap, self.gc, None)
+
+ def getopticon(self, input, (x, y, w, h), ignored_alpha=255):
+ if len(input) == 3:
+ return None
+ pb, = input
+ scale = self.scale
+ newpb = gdk.Pixbuf("rgb", 1, 8, w, h)
+ newpb.fill(0)
+ pb.copy_area(x, y, w, h, newpb, 0, 0)
+ newpb = newpb.scale_simple(int(w*scale), int(h*scale),
+ gdk.INTERP_HYPER)
+ if newpb is None:
+ return None, None, None
+ else:
+ return self.renderpixbuf((newpb,))
+
+ def getppm(self, (x, y, w, h), int=int, ceil=math.ceil):
+ scale = self.scale
+ if isinstance(scale, int):
+ x *= scale
+ y *= scale
+ w *= scale
+ h *= scale
+ else:
+ w = int(ceil((x+w)*scale))
+ h = int(ceil((y+h)*scale))
+ x = int(x*scale)
+ y = int(y*scale)
+ w -= x
+ h -= y
+ bkgnd = gtk.create_pixmap(self.window.window, w, h)
+ bkgnd.draw_drawable(self.gc, self.offscreen, x, y, 0, 0, w, h)
+ return bkgnd, self.gc, None
+
+ def putppm(self, x, y, (pixmap, gc, ignored), rect=None, int=int):
+ if pixmap is None:
+ return
+ scale = self.scale
+ if rect is None:
+ srcx = srcy = 0
+ w = h = 4095
+ else:
+ srcx, srcy, w, h = rect
+ x = int(x*scale)
+ y = int(y*scale)
+ if gc is not self.gc:
+ gc.set_clip_origin(x-srcx, y-srcy)
+ self.offscreen.draw_drawable(gc, pixmap, srcx, srcy, x, y, w, h)
+
+ def flip(self):
+ self.window.window.draw_drawable(self.gc, self.offscreen,
+ 0, 0, 0, 0, self.width, self.height)
+ gdk.flush()
+ self.events_poll()
+
+ def close(self):
+ self.window.destroy()
+
+ def clear(self):
+ self.offscreen.draw_rectangle(self.gc, 1,
+ 0, 0, self.width, self.height)
+
+ def events_poll(self):
+ while gtk.events_pending():
+ gtk.main_iteration()
+
+ def key_press_event(self, window, event):
+ self.events_key.append((event.keyval, KeyPressed))
+
+ def key_release_event(self, window, event):
+ self.events_key.append((event.keyval, KeyReleased))
+
+ def motion_notify_event(self, window, event):
+ self.event_motion = (int(event.x/self.scale), int(event.y/self.scale))
+
+ def button_press_event(self, window, event):
+ self.events_mouse.append((int(event.x/self.scale), int(event.y/self.scale)))
+
+ def keyevents(self):
+ self.events_poll()
+ result = self.events_key
+ self.events_key = []
+ return result
+
+ def pointermotion(self):
+ result = self.event_motion
+ self.event_motion = None
+ return result
+
+ def mouseevents(self):
+ self.events_poll()
+ result = self.events_mouse
+ self.events_mouse = []
+ return result
+
+ def selectlist(self):
+ return []
+
+
+def htmloptionstext(nameval):
+ return 'Scale image by <%s size=5>%%' % (
+ nameval('text', 'zoom', default='100'))
diff --git a/display/dpy_pygame.py b/display/dpy_pygame.py
new file mode 100644
index 0000000..acfd200
--- /dev/null
+++ b/display/dpy_pygame.py
@@ -0,0 +1,245 @@
+
+ ################################################
+## pygame-based implementation of xshm ##
+################################################
+
+import os
+import pygame
+from pygame.locals import *
+from modes import KeyPressed, KeyReleased
+
+
+class Display:
+ musthidemouse = 0
+ mousevisible = 1
+
+ def __init__(self, width, height, title, transparency='yes',
+ fullscreen='no', zoom='100%', smooth='yes',
+ smoothfast='yes'):
+ self.use_transparency = not transparency.startswith('n')
+ self.use_fullscreen = fullscreen.startswith('y')
+ if zoom.endswith('%'):
+ zoom = zoom[:-1]
+ scale = float(zoom) / 100.0
+ iscale = int(scale+0.001)
+ if abs(scale - iscale) < 0.002:
+ scale = iscale
+ self.scale = scale
+ self.smooth = smooth.startswith('y')
+ self.smoothfast = smoothfast.startswith('y')
+
+ # Initialize pygame
+ pygame.init()
+
+ # Set the display mode
+ winstyle = HWSURFACE
+ if self.use_fullscreen:
+ winstyle |= FULLSCREEN
+ bestdepth = pygame.display.mode_ok((int(width * self.scale),
+ int(height * self.scale)),
+ winstyle, 32)
+ self.screen = pygame.display.set_mode((int(width * self.scale),
+ int(height * self.scale)),
+ winstyle, bestdepth)
+ self.offscreen = pygame.Surface((width, height))
+ #decorate the game window
+ pygame.display.set_caption(title)
+ #pygame.mouse.set_visible(0)
+ self.tbcache = None, None
+ self.events_key = []
+ self.events_mouse = []
+ self.prevposition = None
+ EVENT_HANDLERS[KEYDOWN] = self.keydown_handler
+ EVENT_HANDLERS[KEYUP] = self.keyup_handler
+ EVENT_HANDLERS[MOUSEBUTTONDOWN] = self.mousebuttondown_handler
+
+ def keydown_handler(self, e):
+ if e.key == K_ESCAPE and self.use_fullscreen:
+ raise SystemExit # ESC to exit the game if full-screen
+ self.showmouse(not self.musthidemouse)
+ self.events_key.append((e.key, KeyPressed))
+ del self.events_key[:-16]
+
+ def keyup_handler(self, e):
+ self.events_key.append((e.key, KeyReleased))
+ del self.events_key[:-16]
+
+ def mousebuttondown_handler(self, e):
+ self.showmouse(1)
+ self.events_mouse.append(self.fixpos(e.pos))
+ del self.events_mouse[:-8]
+
+ def pixmap(self, w, h, data, colorkey=-1):
+ img = pygame.image.fromstring(data, (w, h), "RGB")
+ if colorkey >= 0:
+ r = colorkey & 0xFF
+ g = (colorkey >> 8) & 0xFF
+ b = (colorkey >> 16) & 0xFF
+ img.set_colorkey([r, g, b])
+ return img # not optimized -- must use getopticon()
+
+ def getopticon(self, pixmap, rect, alpha=255):
+ if not self.use_transparency:
+ alpha = 255
+ img = pixmap.subsurface(rect)
+ colorkey = pixmap.get_colorkey()
+ if alpha == 255 and not colorkey:
+ return img.convert(self.offscreen)
+ else:
+ if colorkey:
+ img.set_colorkey(colorkey, RLEACCEL)
+ if alpha < 255:
+ img.set_alpha(alpha, RLEACCEL)
+ img = img.convert_alpha(self.offscreen)
+ img.set_alpha(255, RLEACCEL)
+ return img
+
+## def vflipppm(self, img):
+## w, h = img.get_size()
+## colorkey = img.get_colorkey()
+## data = pygame.image.tostring(img, "RGB", 1)
+## flipimg = pygame.image.fromstring(data, (w, h), "RGB")
+## flipimg.set_colorkey(colorkey, RLEACCEL)
+## return flipimg, h
+
+ def getppm(self, rect):
+ bkgnd = pygame.Surface(rect[2:])
+ bkgnd.blit(self.offscreen, (0, 0), rect)
+ return bkgnd
+
+ def putppm(self, x, y, bitmap, rect=None):
+ if rect:
+ self.offscreen.blit(bitmap, (x, y), rect)
+ else:
+ self.offscreen.blit(bitmap, (x, y))
+
+ def flip(self):
+ offscreen = self.offscreen
+ if self.scale != 1:
+ w, h = offscreen.get_size()
+ w = int(w * self.scale)
+ h = int(h * self.scale)
+ if self.scale == 2 and self.smoothfast:
+ offscreen = pygame.transform.scale2x(offscreen)
+ elif self.smooth:
+ offscreen = pygame.transform.smoothscale(offscreen, (w, h))
+ else:
+ offscreen = pygame.transform.scale(offscreen, (w, h))
+ self.screen.blit(offscreen, (0, 0))
+ pygame.display.flip()
+ events_dispatch()
+
+ def close(self):
+ self.showmouse(1)
+ pygame.display.quit()
+
+ def clear(self):
+ self.offscreen.fill([0,0,0,])
+
+ def fixpos(self, (x, y)):
+ if self.scale != 1:
+ x = int(x / self.scale)
+ y = int(y / self.scale)
+ return (x, y)
+
+ def events_poll(self):
+ while 1:
+ e = pygame.event.poll()
+ if e.type == NOEVENT:
+ break
+ elif e.type == KEYDOWN:
+ self.events_key.append((e.key, KeyPressed))
+ del self.events_key[:-16]
+ elif e.type == KEYUP:
+ self.events_key.append((e.key, KeyReleased))
+ del self.events_key[:-16]
+ elif e.type == MOUSEBUTTONDOWN:
+ self.events_mouse.append(self.fixpos(e.pos))
+ del self.events_mouse[:-8]
+ elif e.type == ENDMUSICEVENT:
+ self.next_music()
+ elif e.type == QUIT:
+ raise SystemExit
+
+ def keyevents(self):
+ events_dispatch()
+ events = self.events_key
+ self.events_key = []
+ return events
+
+ def pointermotion(self):
+ position = pygame.mouse.get_pos()
+ if position != self.prevposition:
+ self.showmouse(1)
+ self.prevposition = position
+ return self.fixpos(position)
+ else:
+ return None
+
+ def mouseevents(self):
+ events_dispatch()
+ events = self.events_mouse
+ self.events_mouse = []
+ return events
+
+ def selectlist(self):
+ return []
+
+ def taskbar(self, (x, y, w, h)):
+ tbs, tbh = self.tbcache
+ if tbh != h:
+ tbs = pygame.Surface((32, h)).convert_alpha(self.offscreen)
+ alpha_f = 256.0 / h
+ for j in range(h):
+ tbs.fill((128, 128, 255, int(j*alpha_f)),
+ (0, j, 32, 1))
+ self.tbcache = tbs, h
+ for i in range(x, x+w, 32):
+ dw = x+w-i
+ if dw < 32:
+ self.offscreen.blit(tbs, (i, y), (0, 0, dw, h))
+ else:
+ self.offscreen.blit(tbs, (i, y))
+
+ def settaskbar(self, tb_visible):
+ self.showmouse(1)
+ self.musthidemouse = not tb_visible # and self.use_fullscreen
+
+ def showmouse(self, v):
+ if v != self.mousevisible:
+ self.mousevisible = v
+ pygame.mouse.set_visible(v)
+
+
+def quit_handler(e):
+ raise SystemExit
+
+EVENT_HANDLERS = {
+ QUIT: quit_handler,
+ }
+
+def events_dispatch(handlers = EVENT_HANDLERS):
+ while 1:
+ e = pygame.event.poll()
+ if e.type == NOEVENT:
+ break
+ elif handlers.has_key(e.type):
+ handlers[e.type](e)
+
+
+def htmloptionstext(nameval):
+ return '''
+<%s> Full Screen (Esc key to exit)</input><%s><br>
+<%s> Draw slightly transparent bubbles</input><%s><br>
+Scale image by <%s size=5>%%<br>
+<%s> Smoothed scaled image</input><%s><br>
+<%s> Semi-smoothed scaled image (for 200%% only)</input><%s><br>
+''' % (nameval("checkbox", "fullscreen", "yes", default="no"),
+ nameval("hidden", "fullscreen", "no"),
+ nameval("checkbox", "transparency", "yes", default="yes"),
+ nameval("hidden", "transparency", "no"),
+ nameval("text", "zoom", default="100"),
+ nameval("checkbox", "smooth", "yes", default="yes"),
+ nameval("hidden", "smooth", "no"),
+ nameval("checkbox", "smoothfast", "yes", default="no"),
+ nameval("hidden", "smoothfast", "no"))
diff --git a/display/dpy_windows.py b/display/dpy_windows.py
new file mode 100755
index 0000000..b01dfcf
--- /dev/null
+++ b/display/dpy_windows.py
@@ -0,0 +1,23 @@
+import sys
+import wingame
+from modes import BaseDisplay
+from cStringIO import StringIO
+
+
+class Display(BaseDisplay):
+
+ def __init__(self, width, height, title):
+ self.xdpy = xdpy = wingame.Display(width, height)
+ xdpy.settitle(title)
+ self.pixmap = xdpy.pixmap
+ self.getppm = xdpy.getppm
+ self.putppm = xdpy.putppm
+ self.close = xdpy.close
+ self.clear = xdpy.clear
+ self.flip = xdpy.flip
+ self.keyevents = xdpy.keyevents
+ self.mouseevents = xdpy.mouseevents
+ self.pointermotion = xdpy.pointermotion
+
+ def selectlist(self):
+ return []
diff --git a/display/dpy_x.py b/display/dpy_x.py
new file mode 100644
index 0000000..ffca90f
--- /dev/null
+++ b/display/dpy_x.py
@@ -0,0 +1,40 @@
+import sys
+import xshm
+from modes import BaseDisplay
+from cStringIO import StringIO
+
+
+class Display(BaseDisplay):
+
+ def __init__(self, width, height, title, shm='yes'):
+ use_shm = not shm.startswith('n')
+ self.xdpy = xdpy = xshm.Display(width, height, use_shm)
+ self.pixmap = xdpy.pixmap
+ self.getppm = xdpy.getppm
+ self.putppm = xdpy.putppm
+ self.overlayppm = xdpy.overlayppm
+ self.close = xdpy.close
+ self.clear = xdpy.clear
+ self.flip = xdpy.flip
+ self.keyevents = xdpy.keyevents
+ self.mouseevents = xdpy.mouseevents
+ self.pointermotion = xdpy.pointermotion
+ if use_shm and not xdpy.shmmode():
+ print >> sys.stderr, \
+ "Note: cannot use SHM extension (%dx%d), display will be slow." % \
+ (width, height)
+
+ def selectlist(self):
+ if hasattr(self.xdpy, 'fd'):
+ from socket import fromfd, AF_INET, SOCK_STREAM
+ return [fromfd(self.xdpy.fd(), AF_INET, SOCK_STREAM)]
+ else:
+ return []
+
+
+def htmloptionstext(nameval):
+ return '''
+<%s> Use the shared memory extension</input><%s><br>
+<font size=-1>Note: Disable it for remote connections or old X servers</font>
+''' % (nameval("checkbox", "shm", "yes", default="yes"),
+ nameval("hidden", "shm", "no"))
diff --git a/display/modes.py b/display/modes.py
new file mode 100644
index 0000000..ad0c1c0
--- /dev/null
+++ b/display/modes.py
@@ -0,0 +1,196 @@
+import sys
+
+KeyPressed = 2
+KeyReleased = 3
+
+
+class BaseDisplay:
+ __taskbkgnd = None
+
+ def taskbar(self, (x, y, w, h)):
+ if self.__taskbkgnd is None:
+ pixel = "\x00\x00\x80"
+ hole = "\x01\x01\x01"
+ self.__taskbkgnd = self.pixmap(32, 32,
+ ((pixel+hole)*16 + (hole+pixel)*16) * 16, 0x010101)
+ for j in range(y, y+h, 32):
+ for i in range(x, x+w, 32):
+ self.putppm(i, j, self.__taskbkgnd,
+ (0, 0, x+w-i, y+h-j))
+
+
+class Mode:
+ low_priority = 0
+
+ def __init__(self, name, descr, extraoptsdescr,
+ options={}, url=None):
+ self.name = name
+ self.descr = descr
+ self.extraoptsdescr = extraoptsdescr
+ self.options = options.copy()
+ self.url = url
+
+ def getmodule(self):
+ return __import__(self.prefix + self.name.lower(), globals(),
+ locals(), ['available'])
+
+ def imperror(self):
+ try:
+ return self.__imperror
+ except AttributeError:
+ try:
+ module = self.getmodule()
+ except ImportError:
+ result = 'not installed'
+ else:
+ result = hasattr(module, 'imperror') and module.imperror()
+ self.__imperror = result
+ return result
+
+ def unique_id(self):
+ return self.prefix + self.name
+
+ def printline(self, f):
+ err = self.imperror()
+ if err:
+ state = ' [%s]' % err
+ else:
+ state = ''
+ print >> f, ' %-8s %s%s' % (self.name, self.descr, state)
+ if self.url:
+ print >> f, ' %s' % self.url
+ for line in self.extraoptsdescr:
+ print >> f, ' %s' % line
+
+ def getformaloptions(self):
+ return '', [c+'=' for c in self.options.keys()]
+
+ def setoptions(self, options):
+ for key in self.options.keys():
+ if options.has_key('--'+key):
+ self.options[key] = options['--'+key]
+
+ def currentdriver(self):
+ lst = self.options.items()
+ lst.sort()
+ lst = ['--%s=%s' % keyvalue for keyvalue in lst]
+ return ' '.join([self.name] + lst)
+
+ def htmloptionstext(self, *args):
+ if self.imperror():
+ return None
+ module = self.getmodule()
+ return (hasattr(module, 'htmloptionstext') and
+ module.htmloptionstext(*args))
+
+
+class GraphicMode(Mode):
+ prefix = 'dpy_'
+
+
+class SoundMode(Mode):
+ prefix = 'snd_'
+
+
+def graphicmodeslist():
+ return [
+ GraphicMode('X', 'XWindow (Linux/Unix)',
+ ['--shm=yes use the Shared Memory extension (default)',
+ '--shm=no disable it (for remote connections or old X servers)',
+ ],
+ {'shm': 'yes'}),
+ GraphicMode('windows', 'MS Windows', []),
+ GraphicMode('pygame', 'PyGame library (all platforms)',
+ ['--fullscreen=yes go full screen (Esc key to exit)',
+ '--transparency=yes slightly transparent bubbles (default)',
+ '--transparency=no disable it (a bit faster)',
+ '--zoom=xxx% scale image by xxx %',
+ '--smooth smoothed scaled image',
+ '--smoothfast semi-smoothed, for 200% scale only'],
+ {'transparency': 'yes', 'fullscreen': 'no',
+ 'zoom': '100', 'smooth': 'yes', 'smoothfast': 'no'},
+ url='http://www.pygame.org'),
+ GraphicMode('gtk', 'PyGTK (Gnome)',
+ ['--zoom=xxx% scale image by xxx %'],
+ {'zoom': '100'},
+ url='http://www.pygtk.org/'),
+ ]
+
+def soundmodeslist():
+ return [
+ SoundMode('pygame', 'PyGame library mixer (all platforms)',
+ [], url='http://www.pygame.org'),
+ SoundMode('linux', 'audio mixer for Linux',
+ ['--freq=# mixer frequency (default 44100)',
+ '--fmt=# data format (default S16_NE, --fmt=list for a list)'],
+ {'freq': '44100', 'fmt': 'S16_NE'}),
+ SoundMode('windows', 'audio mixer for Windows',
+ ['--freq=# mixer frequency (default 44100)',
+ '--bits=# bits per sample (8 or default 16)'],
+ {'freq': '44100', 'bits': '16'}),
+ SoundMode('off', 'no sounds', []),
+ ]
+
+def findmode(name, lst):
+ if name is None:
+ # find the first installed mode
+ last_chance = None
+ for info in lst:
+ err = info.imperror()
+ if err:
+ continue
+ if info.low_priority:
+ if last_chance is None:
+ last_chance = info
+ else:
+ return info
+ if last_chance is not None:
+ return last_chance
+ raise KeyError, 'no driver available!'
+ else:
+ # find mode by name
+ for info in lst:
+ if info.name.upper() == name.upper():
+ err = info.imperror()
+ if err:
+ raise KeyError, '%s: %s' % (info.name, err)
+ return info
+ raise KeyError, '%s: no such driver' % name
+
+def findmode_err(*args):
+ try:
+ return findmode(*args)
+ except KeyError, e:
+ print >> sys.stderr, str(e)
+ sys.exit(1)
+
+def open_dpy(mode, width, height, title):
+ driver, sound, extraopts = mode
+ ginfo = findmode_err(driver, graphicmodeslist())
+ ginfo.setoptions(extraopts)
+ dpy = ginfo.getmodule().Display(width, height, title, **ginfo.options)
+ print 'graphics driver:', ginfo.currentdriver()
+ return dpy
+
+def open_snd(mode):
+ driver, sound, extraopts = mode
+ sinfo = findmode_err(sound, soundmodeslist())
+ sinfo.setoptions(extraopts)
+ snd = sinfo.getmodule().Sound(**sinfo.options)
+ if snd.has_sound:
+ sinfo.options['music'] = 'yes'
+ sinfo.setoptions(extraopts)
+ if (sinfo.options['music'].startswith('n') or
+ sinfo.options['music'] == 'off'):
+ snd.has_music = 0
+ print 'sound driver:', sinfo.currentdriver()
+ return snd
+ else:
+ return None
+
+
+def musichtmloptiontext(nameval):
+ return '''<font size=-1>
+<%s> Background music</input><%s>
+</font>''' % (nameval("checkbox", "music", "yes", default="yes", mangling=0),
+ nameval("hidden", "music", "no", mangling=0))
diff --git a/display/music1.py b/display/music1.py
new file mode 100644
index 0000000..b5c0c6d
--- /dev/null
+++ b/display/music1.py
@@ -0,0 +1,33 @@
+class Music:
+ def __init__(self, filename):
+ self.filename = filename
+ self.w = None
+ self.sampledata = ''
+ def openchannel(self):
+ if self.w is not None:
+ self.w.close()
+ import wave
+ self.w = w = wave.open(open(self.filename, 'rb'), 'r')
+ self.w_params = (w.getnchannels(),
+ w.getsampwidth(),
+ w.getframerate())
+ chan, width, freq = self.w_params
+ self.dataleft = w.getnframes() * (chan*width)
+ self.sampledata = ''
+ def decode(self, mixer, bytecount):
+ result = self.sampledata
+ if not result and self.dataleft > 0:
+ # decode and convert some more data
+ chan, width, freq = self.w_params
+ #framecount = bytecount / (chan*width)
+ inputdata = self.w.readframes(bytecount) #(framecount)
+ self.dataleft -= len(inputdata)
+ result = mixer.resample(inputdata,
+ freq = freq,
+ bits = width * 8,
+ signed = width > 1,
+ channels = chan,
+ byteorder = 'little')
+ #print len(result)
+ self.sampledata = result[bytecount:]
+ return result[:bytecount]
diff --git a/display/pclient.py b/display/pclient.py
new file mode 100644
index 0000000..c6546ce
--- /dev/null
+++ b/display/pclient.py
@@ -0,0 +1,860 @@
+#! /usr/bin/env python
+
+import sys, os
+from socket import *
+from select import select
+import struct, zlib
+import time
+from common.msgstruct import *
+from common.pixmap import decodepixmap
+from common import hostchooser
+import modes
+from modes import KeyPressed, KeyReleased
+import caching
+
+#import psyco; psyco.full()
+
+# switch to udp_over_tcp if the udp socket didn't receive at least 60% of
+# the packets sent by the server
+UDP_EXPECTED_RATIO = 0.60
+
+
+def loadpixmap(dpy, data, colorkey=None):
+ w, h, data = decodepixmap(data)
+ if colorkey is None:
+ colorkey = -1
+ elif colorkey < 0:
+ r, g, b = struct.unpack("BBB", self.data[:3])
+ colorkey = b | (g<<8) | (r<<16)
+ return dpy.pixmap(w, h, data, colorkey)
+
+class Icon:
+ alpha = 255
+ def __init__(self, playfield):
+ self.playfield = playfield
+ self.size = 0, 0
+ def __getattr__(self, attr):
+ if attr == 'pixmap':
+ self.pixmap = self.playfield.getpixmap(self.bmpcode)
+ if hasattr(self.playfield.dpy, 'getopticon'):
+ ico = self.playfield.dpy.getopticon(
+ self.pixmap, self.originalrect, self.alpha)
+ if ico is not None:
+ self.pixmap = ico
+ self.rect = None
+ return self.pixmap
+ elif attr in ('bmpcode', 'rect'):
+ raise KeyError, attr
+ elif attr == 'originalrect':
+ self.originalrect = self.rect
+ return self.originalrect
+ raise AttributeError, attr
+ def clear(self):
+ if self.__dict__.has_key('pixmap'):
+ del self.pixmap
+
+class DataChunk(caching.Data):
+ SOURCEDIR = os.path.abspath(os.path.join(os.path.dirname(caching.__file__),
+ os.pardir))
+ CACHEDIR = os.path.join(SOURCEDIR, 'cache')
+ TOTAL = 0
+
+ def __init__(self, fileid):
+ caching.Data.__init__(self)
+ self.fileid = fileid
+ self.pending = []
+ self.progresshook = None
+
+ def server_md5(self, playfield, filename, position, length, checksum):
+ if not self.loadfrom(filename, position, length, checksum):
+ self.pending.append((0, position))
+ playfield.s.sendall(message(CMSG_DATA_REQUEST, self.fileid,
+ position, length))
+
+ def server_patch(self, position, data, lendata):
+ #print 'server_patch', self.fileid, position, len(data)
+ prev = DataChunk.TOTAL >> 10
+ DataChunk.TOTAL += lendata
+ total = DataChunk.TOTAL >> 10
+ if total != prev:
+ print "downloaded %dkb of data from server" % total
+ self.store(position, data)
+ try:
+ self.pending.remove((0, position))
+ except ValueError:
+ pass
+ else:
+ while self.pending and self.pending[0][0]:
+ callback = self.pending[0][1]
+ del self.pending[0]
+ callback(self)
+
+ def when_ready(self, callback):
+ if self.pending:
+ self.pending.append((1, callback))
+ else:
+ callback(self)
+
+
+class Playfield:
+ TASKBAR_HEIGHT = 48
+
+ def __init__(self, s, sockaddr):
+ self.s = s
+ self.sockaddr = sockaddr
+ try:
+ self.s.setsockopt(SOL_IP, IP_TOS, 0x10) # IPTOS_LOWDELAY
+ except error, e:
+ print >> sys.stderr, "Cannot set IPTOS_LOWDELAY:", str(e)
+ try:
+ self.s.setsockopt(SOL_TCP, TCP_NODELAY, 1)
+ except error, e:
+ print >> sys.stderr, "Cannot set TCP_NODELAY:", str(e)
+
+ initialbuf = ""
+ while 1:
+ t = self.s.recv(200)
+ if not t and not hasattr(self.s, 'RECV_CAN_RETURN_EMPTY'):
+ raise error, "connexion closed"
+ initialbuf += t
+ if len(initialbuf) >= len(MSG_WELCOME):
+ head = initialbuf[:len(MSG_WELCOME)]
+ tail = initialbuf[len(MSG_WELCOME):]
+ if head != MSG_WELCOME:
+ raise error, "connected to something not a game server"
+ if '\n' in tail:
+ break
+ n = tail.index('\n')
+ line2 = tail[:n]
+ self.initialbuf = tail[n+1:]
+
+ self.gameident = line2.strip()
+## self.datapath = None
+## if self.gameident.endswith(']'):
+## i = self.gameident.rfind('[')
+## if i >= 0:
+## self.gameident, self.datapath = (self.gameident[:i].strip(),
+## self.gameident[i+1:-1])
+ print "connected to %r." % self.gameident
+ self.s.sendall(message(CMSG_PROTO_VERSION, 3))
+
+ def setup(self, mode, udp_over_tcp):
+ self.playing = {} # 0, 1, or 'l' for local
+ self.keys = {}
+ self.keycodes = {}
+ self.last_key_event = (None, None)
+ self.dpy = None
+ self.snd = None
+ self.pixmaps = {} # {bmpcode: dpy_pixmap}
+ self.bitmaps = {} # {bmpcode: (fileid_or_data, colorkey)}
+ self.icons = {}
+ self.sounds = {}
+ self.currentmusic = None
+ self.fileids = {}
+ self.sprites = []
+ self.playingsounds = {}
+ self.playericons = {}
+ self.screenmode = mode
+ self.initlevel = 0
+ if mode[-1].has_key('udp_over_tcp'):
+ udp_over_tcp = mode[-1]['udp_over_tcp']
+ self.trackcfgmtime = None
+ if mode[-1].has_key('cfgfile'):
+ self.trackcfgfile = mode[-1]['cfgfile']
+ else:
+ self.trackcfgfile = os.path.join(DataChunk.SOURCEDIR,
+ 'http2', 'config.txt')
+ self.udpsock = None
+ self.udpsock_low = None
+ self.udpsock2 = None
+ self.accepted_broadcast = 0
+ self.tcpbytecounter = 0
+ self.udpbytecounter = 0
+ if udp_over_tcp == 1:
+ self.start_udp_over_tcp()
+ else:
+ self.pending_udp_data = None
+ if udp_over_tcp == 'auto':
+ self.udpsock_low = 0
+ self.dyndecompress = [[None, None, None, None] for i in range(8)]
+ self.dynnorepeat = None
+
+ def run(self, mode, udp_over_tcp='auto'):
+ self.setup(mode, udp_over_tcp)
+ try:
+ self.mainloop()
+ finally:
+ if self.dpy:
+ self.dpy.close()
+ try:
+ self.s.close()
+ except:
+ pass
+
+ def mainloop(self):
+ pss = hostchooser.serverside_ping()
+ self.initial_iwtd = [self.s, pss]
+ self.iwtd = self.initial_iwtd[:]
+ self.animdelay = 0.0
+ inbuf = self.process_inbuf(self.initialbuf)
+ self.initialbuf = ""
+ errors = 0
+ while 1:
+ if self.dpy:
+ self.processkeys()
+ iwtd, owtd, ewtd = select(self.iwtd, [], [], self.animdelay)
+ self.animdelay = 0.5
+ if self.dpy:
+ self.processkeys()
+ if self.s in iwtd:
+ inputdata = self.s.recv(0x6000)
+ self.tcpbytecounter += len(inputdata)
+ inbuf += inputdata
+ inbuf = self.process_inbuf(inbuf)
+ if self.dpy:
+ if self.udpsock in iwtd:
+ udpdata1 = None
+ while self.udpsock in iwtd:
+ try:
+ udpdata = self.udpsock.recv(65535)
+ except error, e:
+ print >> sys.stderr, e
+ errors += 1
+ if errors > 10:
+ raise
+ break
+ self.udpbytecounter += len(udpdata)
+ if len(udpdata) > 3 and '\x80' <= udpdata[0] < '\x90':
+ udpdata = self.dynamic_decompress(udpdata)
+ if udpdata is not None:
+ udpdata1 = udpdata
+ iwtd, owtd, ewtd = select(self.iwtd, [], [], 0)
+ if udpdata1 is not None:
+ self.update_sprites(udpdata1)
+ if self.udpsock2 in iwtd:
+ while self.udpsock2 in iwtd:
+ udpdata = self.udpsock2.recv(65535)
+ self.udpbytecounter += len(udpdata)
+ if udpdata == BROADCAST_MESSAGE:
+ if not self.accepted_broadcast:
+ self.s.sendall(message(CMSG_UDP_PORT, '*'))
+ self.accepted_broadcast = 1
+ #self.udpsock_low = None
+ udpdata = ''
+ iwtd, owtd, ewtd = select(self.iwtd, [], [], 0)
+ if udpdata and self.accepted_broadcast:
+ self.update_sprites(udpdata)
+ if self.pending_udp_data:
+ self.update_sprites(self.pending_udp_data)
+ self.pending_udp_data = ''
+ erasetb = self.taskbarmode and self.draw_taskbar()
+ d = self.dpy.flip()
+ if d:
+ self.animdelay = min(self.animdelay, d)
+ if self.snd:
+ d = self.snd.flop()
+ if d:
+ self.animdelay = min(self.animdelay, d)
+ if erasetb:
+ self.erase_taskbar(erasetb)
+ if pss in iwtd:
+ hostchooser.answer_ping(pss, self.gameident, self.sockaddr)
+
+ def process_inbuf(self, inbuf):
+ while inbuf:
+ values, inbuf = decodemessage(inbuf)
+ if not values:
+ break # incomplete message
+ fn = Playfield.MESSAGES.get(values[0], self.msg_unknown)
+ fn(self, *values[1:])
+ return inbuf
+
+ def dynamic_decompress(self, udpdata):
+ # Format of a UDP version 3 packet:
+ # header byte: 0x80 - 0x87 packet from thread 0 - 7
+ # or 0x88 - 0x8F reset packet from thread 0 - 7
+ # previous frame in same thread (1 byte)
+ # frame number (1 byte)
+ thread = self.dyndecompress[ord(udpdata[0]) & 7]
+ # thread==[decompress, lastframenumber, recompressed, lastframedata]
+ prevframe = udpdata[1]
+ thisframe = udpdata[2]
+ #print '---'
+ #for t in self.dyndecompress:
+ # print repr(t)[:120]
+ #print
+ #print `udpdata[:3]`
+
+ if udpdata[0] >= '\x88':
+ # reset
+ d = zlib.decompressobj().decompress
+ if prevframe != thisframe: # if not global sync point
+ # sync point from a previous frame
+ # find all threads with the same prevframe
+ threads = [t for t in self.dyndecompress if prevframe == t[1]]
+ if not threads:
+ return None # lost
+ # find a thread with already-recompressed data
+ for t in threads:
+ if t[2]:
+ data = t[3]
+ break
+ else:
+ # recompress and cache the prevframe data
+ t = threads[0]
+ data = t[3]
+ co = zlib.compressobj(6)
+ data = co.compress(data) + co.flush(zlib.Z_SYNC_FLUSH)
+ t[2] = 1
+ t[3] = data
+ d(data) # use it to initialize the state of the decompressobj
+ #print d
+ thread[0] = d
+ elif prevframe != thread[1]:
+ #print 'lost'
+ return None # lost
+ else:
+ d = thread[0]
+ # go forward in thread
+ try:
+ framedata = d(udpdata[3:])
+ #print d
+ thread[1] = thisframe
+ thread[2] = 0
+ thread[3] = framedata
+ if thisframe == self.dynnorepeat:
+ return None
+ self.dynnorepeat = thisframe
+ return framedata
+ except zlib.error:
+ #print 'crash'
+ return None
+
+ def geticon(self, icocode):
+ try:
+ return self.icons[icocode]
+ except KeyError:
+ ico = self.icons[icocode] = Icon(self)
+ return ico
+
+ def getpixmap(self, bmpcode):
+ try:
+ return self.pixmaps[bmpcode]
+ except KeyError:
+ data, colorkey = self.bitmaps[bmpcode]
+ if type(data) is type(''):
+ data = zlib.decompress(data)
+ else:
+ if data.pending:
+ raise KeyError
+ data = data.read()
+ pixmap = loadpixmap(self.dpy, data, colorkey)
+ self.pixmaps[bmpcode] = pixmap
+ return pixmap
+
+ def update_sprites(self, udpdata):
+ sprites = self.sprites
+ unpack = struct.unpack
+
+ currentsounds = {}
+ base = 0
+ while udpdata[base+4:base+6] == '\xFF\xFF':
+ key, lvol, rvol = struct.unpack("!hBB", udpdata[base:base+4])
+ try:
+ snd = self.sounds[key]
+ except KeyError:
+ pass # ignore sounds with bad code (probably not defined yet)
+ else:
+ n = self.playingsounds.get(key)
+ if n:
+ currentsounds[key] = n-1
+ elif self.snd:
+ self.snd.play(snd,
+ lvol / 255.0,
+ rvol / 255.0)
+ currentsounds[key] = 4
+ base += 6
+ self.playingsounds = currentsounds
+
+ for j in range(len(sprites)):
+ if sprites[j][0] != udpdata[base:base+6]:
+ removes = sprites[j:]
+ del sprites[j:]
+ removes.reverse()
+ eraser = self.dpy.putppm
+ for reserved, eraseargs in removes:
+ eraser(*eraseargs)
+ break
+ base += 6
+ #print "%d sprites redrawn" % (len(udpdata)/6-j)
+ try:
+ overlayer = self.dpy.overlayppm
+ except AttributeError:
+ getter = self.dpy.getppm
+ setter = self.dpy.putppm
+ for j in range(base, len(udpdata)-5, 6):
+ info = udpdata[j:j+6]
+ x, y, icocode = unpack("!hhh", info[:6])
+ try:
+ ico = self.icons[icocode]
+ sprites.append((info, (x, y, getter((x, y) + ico.size))))
+ setter(x, y, ico.pixmap, ico.rect)
+ except KeyError:
+ #print "bad ico code", icocode
+ pass # ignore sprites with bad ico (probably not defined yet)
+ else:
+ for j in range(base, len(udpdata)-5, 6):
+ info = udpdata[j:j+6]
+ x, y, icocode = unpack("!hhh", info[:6])
+ try:
+ ico = self.icons[icocode]
+ overlay = overlayer(x, y, ico.pixmap, ico.rect, ico.alpha)
+ sprites.append((info, overlay))
+ except KeyError:
+ #print "bad ico code", icocode
+ pass # ignore sprites with bad ico (probably not defined yet)
+
+ t0, n = self.painttimes
+ n = n + 1
+ if n == 50:
+ t = time.time()
+ t, t0 = t-t0, t
+ if t:
+ print "%.2f images per second, %.1f kbytes per second" % (
+ float(n)/t,
+ float(self.tcpbytecounter+self.udpbytecounter)/1024/t)
+ self.tcpbytecounter = -self.udpbytecounter
+ n = 0
+ self.painttimes = t0, n
+
+ def get_taskbar(self):
+ y0 = self.height - self.TASKBAR_HEIGHT
+ iconlist = []
+ f = 1.5 * time.time()
+ f = f-int(f)
+ pi = self.playericons.items()
+ pi.sort()
+ xpos = 0
+ for id, ico in pi:
+ if self.playing.get(id) != 'l':
+ w, h = ico.size
+ xpos += int(w * 5 / 3)
+ if not self.playing.get(id):
+ y = self.height - h
+ if self.keydefinition and id == self.keydefinition[0]:
+ num, icons = self.keys[self.nextkeyname()]
+ ico = icons[int(f*len(icons))-1]
+ y = y0 + int((self.TASKBAR_HEIGHT-ico.size[1])/2)
+ self.animdelay = 0.04
+ iconlist.append((xpos-w, y, ico, id))
+ pi.reverse()
+ f = f * (1.0-f) * 4.0
+ xpos = self.width
+ for id, ico in pi:
+ if self.playing.get(id) == 'l':
+ w, h = ico.size
+ xpos -= int(w * 5 / 3)
+ dy = self.TASKBAR_HEIGHT - h - 1
+ y = self.height - h - int(dy*f)
+ iconlist.append((xpos, y, ico, id))
+ self.animdelay = 0.04
+ return y0, iconlist
+
+ def clic_taskbar(self, (cx,cy)):
+ y0, icons = self.get_taskbar()
+ if cy >= y0:
+ for x, y, ico, id in icons:
+ if x <= cx < x+ico.size[0]:
+ return id
+ return None
+
+ def draw_taskbar(self):
+ y0, icons = self.get_taskbar()
+ rect = (0, y0, self.width, self.TASKBAR_HEIGHT)
+ bkgnd = self.dpy.getppm(rect)
+ self.dpy.taskbar(rect)
+ for x, y, ico, id in icons:
+ try:
+ self.dpy.putppm(x, y, ico.pixmap, ico.rect)
+ except KeyError:
+ pass
+ return y0, bkgnd
+
+ def erase_taskbar(self, (y0, bkgnd)):
+ self.dpy.putppm(0, y0, bkgnd)
+
+ def nextkeyname(self):
+ pid, df = self.keydefinition
+ undef = [(num, keyname) for keyname, (num, icons) in self.keys.items()
+ if not df.has_key(keyname) and icons]
+ if undef:
+ num, keyname = min(undef)
+ return keyname
+ else:
+ return None
+
+ def startplaying(self):
+ args = ()
+ if hasattr(self.s, 'udp_over_udp_mixer'):
+ # for SocketOverUdp: reuse the UDP address
+ port = self.s.getsockname()[1]
+ self.udpsock_low = None
+ self.s.udp_over_udp_decoder = self.udp_over_udp_decoder
+ self.start_udp_over_tcp()
+ elif self.pending_udp_data is not None:
+ port = MSG_INLINE_FRAME
+ else:
+ if '*udpsock*' in PORTS:
+ self.udpsock, (host, port) = PORTS['*udpsock*']
+ args = (host,)
+ else:
+ self.udpsock = socket(AF_INET, SOCK_DGRAM)
+ self.udpsock.bind(('', PORTS.get('CLIENT', INADDR_ANY)))
+ host, port = self.udpsock.getsockname()
+ # Send a dummy UDP message to the server. Some NATs will
+ # then let through the UDP messages from the server.
+ self.udpsock.sendto('.', self.s.getpeername())
+ self.iwtd.append(self.udpsock)
+ self.initial_iwtd.append(self.udpsock)
+ if 'sendudpto' in PORTS:
+ args = (PORTS['sendudpto'],)
+ outbound = []
+ outbound.append(message(CMSG_UDP_PORT, port, *args))
+ if self.snd and self.snd.has_music:
+ outbound.append(message(CMSG_ENABLE_MUSIC, 1))
+ outbound.append(message(CMSG_PING))
+ self.s.sendall(''.join(outbound))
+
+ def start_udp_over_tcp(self):
+ self.pending_udp_data = ''
+ self.udp_over_tcp_decompress = zlib.decompressobj().decompress
+ self.udpsock_low = None
+ for name in ('udpsock', 'udpsock2'):
+ sock = getattr(self, name)
+ if sock is not None:
+ try:
+ self.iwtd.remove(sock)
+ except ValueError:
+ pass
+ try:
+ self.initial_iwtd.remove(sock)
+ except ValueError:
+ pass
+ sock.close()
+ setattr(self, name, None)
+
+ def udp_over_udp_decoder(self, udpdata):
+ if len(udpdata) > 3 and '\x80' <= udpdata[0] < '\x90':
+ data = self.dynamic_decompress(udpdata)
+ if data:
+ self.pending_udp_data = data
+
+ def processkeys(self):
+ keyevents = self.dpy.keyevents()
+ if keyevents:
+ now = time.time()
+ pending = {}
+ for keysym, event in keyevents:
+ pending[keysym] = event
+ for keysym, event in pending.items():
+ code = self.keycodes.get((keysym, event))
+ if code and self.playing.get(code[0]) == 'l':
+ if (code == self.last_key_event[0] and
+ now - self.last_key_event[1] < 0.77):
+ continue # don't send too much events for auto-repeat
+ self.last_key_event = code, now
+ self.s.sendall(code[1])
+ elif self.keydefinition:
+ self.define_key(keysym)
+ pointermotion = self.dpy.pointermotion()
+ if pointermotion:
+ x, y = pointermotion
+ self.settaskbar(y >= self.height - 2*self.TASKBAR_HEIGHT)
+ mouseevents = self.dpy.mouseevents()
+ if mouseevents:
+ self.settaskbar(1)
+ self.keydefinition = None
+ for clic in mouseevents:
+ clic_id = self.clic_taskbar(clic)
+ if clic_id is not None:
+ if self.playing.get(clic_id) == 'l':
+ self.s.sendall(message(CMSG_REMOVE_PLAYER, clic_id))
+ else:
+ self.keydefinition = clic_id, {}
+ if self.taskbartimeout is not None and time.time() > self.taskbartimeout:
+ self.settaskbar(0)
+
+ def settaskbar(self, nmode):
+ self.taskbartimeout = None
+ if self.taskbarfree:
+ self.taskbarmode = (nmode or
+ 'l' not in self.playing.values() or
+ (self.keydefinition is not None))
+ if nmode:
+ self.taskbartimeout = time.time() + 5.0
+ if hasattr(self.dpy, 'settaskbar'):
+ self.dpy.settaskbar(self.taskbarmode)
+
+ def define_key(self, keysym):
+ clic_id, df = self.keydefinition
+ if keysym in df.values():
+ return
+ df[self.nextkeyname()] = keysym
+ if self.nextkeyname() is not None:
+ return
+ self.keydefinition = None
+ self.s.sendall(message(CMSG_ADD_PLAYER, clic_id))
+ for keyname, (num, icons) in self.keys.items():
+ if keyname[:1] == '-':
+ event = KeyReleased
+ keyname = keyname[1:]
+ else:
+ event = KeyPressed
+ if df.has_key(keyname):
+ keysym = df[keyname]
+ self.keycodes[keysym, event] = \
+ clic_id, message(CMSG_KEY, clic_id, num)
+
+ def msg_unknown(self, *rest):
+ print >> sys.stderr, "?"
+
+ def msg_player_join(self, id, local, *rest):
+ if local:
+ self.playing[id] = 'l'
+ self.settaskbar(0)
+ self.checkcfgfile(1)
+ else:
+ self.playing[id] = 1
+
+ def msg_player_kill(self, id, *rest):
+ self.playing[id] = 0
+ for key, (pid, msg) in self.keycodes.items():
+ if pid == id:
+ del self.keycodes[key]
+
+ def msg_broadcast_port(self, port):
+ if self.pending_udp_data is not None:
+ return
+ if self.udpsock2 is not None:
+ try:
+ self.iwtd.remove(self.udpsock2)
+ except ValueError:
+ pass
+ try:
+ self.initial_iwtd.remove(self.udpsock2)
+ except ValueError:
+ pass
+ self.udpsock2.close()
+ self.udpsock2 = None
+ self.accepted_broadcast = 0
+ try:
+ self.udpsock2 = socket(AF_INET, SOCK_DGRAM)
+ self.udpsock2.bind(('', port))
+ self.udpsock2.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
+ except error, e:
+ print "Cannot listen on the broadcast port %d" % port, str(e)
+ self.udpsock2 = None
+ else:
+ self.iwtd.append(self.udpsock2)
+ self.initial_iwtd.append(self.udpsock2)
+
+ def msg_def_playfield(self, width, height, backcolor=None,
+ gameident=None, *rest):
+ #if self.snd is not None:
+ # self.snd.close()
+ if self.dpy is not None:
+ # clear all pixmaps
+ for ico in self.icons.values():
+ ico.clear()
+ self.pixmaps.clear()
+ self.dpy.close()
+ del self.sprites[:]
+ self.width = width
+ self.height = height
+ if gameident:
+ self.gameident = gameident
+ self.dpy = modes.open_dpy(self.screenmode, width, height, self.gameident)
+ self.snd = self.snd or modes.open_snd(self.screenmode)
+ if self.snd:
+ self.s.sendall(message(CMSG_ENABLE_SOUND))
+ self.iwtd = self.dpy.selectlist() + self.initial_iwtd
+ self.dpy.clear() # backcolor is ignored
+ self.painttimes = (time.time(), 0)
+ self.s.sendall(message(CMSG_PING))
+ self.taskbarmode = 0
+ self.taskbarfree = 0
+ self.taskbartimeout = None
+ self.keydefinition = None
+
+ def msg_def_key(self, name, num, *icons):
+ self.keys[name] = num, [self.geticon(ico) for ico in icons]
+
+ def msg_def_icon(self, bmpcode, icocode, x, y, w, h, alpha=255, *rest):
+## if h<0:
+## try:
+## bitmap, height = self.flippedbitmaps[bmpcode]
+## except KeyError:
+## bitmap, height = self.dpy.vflipppm(self.bitmaps[bmpcode])
+## self.flippedbitmaps[bmpcode] = bitmap, height
+## y = height - y
+## h = - h
+## else:
+ ico = self.geticon(icocode)
+ ico.bmpcode = bmpcode
+ ico.rect = x, y, w, h
+ ico.size = w, h
+ if alpha < 255:
+ ico.alpha = alpha
+
+ def msg_def_bitmap(self, bmpcode, data, colorkey=None, *rest):
+ if type(data) is not type(''):
+ data = self.fileids[data]
+ self.bitmaps[bmpcode] = data, colorkey
+
+ def msg_def_sample(self, smpcode, data, *rest):
+ def ready(f, self=self, smpcode=smpcode):
+ if self.snd:
+ self.sounds[smpcode] = self.snd.sound(f)
+ f.clear()
+ if type(data) is type(''):
+ data = zlib.decompress(data)
+ f = DataChunk(None)
+ f.store(0, data)
+ ready(f)
+ else:
+ f = self.fileids[data]
+ f.when_ready(ready)
+
+ def msg_patch_file(self, fileid, position, data, lendata=None, *rest):
+ if self.fileids.has_key(fileid):
+ f = self.fileids[fileid]
+ else:
+ f = self.fileids[fileid] = DataChunk(fileid)
+ f.server_patch(position, data, lendata or len(data))
+
+ def msg_zpatch_file(self, fileid, position, data, *rest):
+ data1 = zlib.decompress(data)
+ self.msg_patch_file(fileid, position, data1, len(data), *rest)
+
+ def msg_md5_file(self, fileid, filename, position, length, checksum, *rest):
+ if self.fileids.has_key(fileid):
+ f = self.fileids[fileid]
+ else:
+ f = self.fileids[fileid] = DataChunk(fileid)
+ f.server_md5(self, filename, position, length, checksum)
+
+ def msg_play_music(self, loop_from, *codes):
+ codes = [self.fileids[c] for c in codes]
+ self.currentmusic = loop_from, codes, list(codes)
+ self.activate_music()
+
+ def activate_music(self, f=None):
+ loop_from, codes, checkcodes = self.currentmusic
+ if checkcodes:
+ checkcodes.pop().when_ready(self.activate_music)
+ elif self.snd:
+ self.snd.play_musics(codes, loop_from)
+
+ def msg_fadeout(self, time, *rest):
+ if self.snd:
+ self.snd.fadeout(time)
+
+ def msg_player_icon(self, pid, icocode, *rest):
+ self.playericons[pid] = self.geticon(icocode)
+
+ def checkcfgfile(self, force=0):
+ if self.trackcfgfile:
+ try:
+ st = os.stat(self.trackcfgfile)
+ except OSError:
+ pass
+ else:
+ if force or (st.st_mtime != self.trackcfgmtime):
+ self.trackcfgmtime = st.st_mtime
+ try:
+ f = open(self.trackcfgfile, 'r')
+ data = f.read().strip()
+ f.close()
+ d = eval(data or '{}', {}, {})
+ except:
+ print >> sys.stderr, 'Invalid config file format'
+ else:
+ d = d.get(gethostname(), {})
+ namemsg = ''
+ for id, local in self.playing.items():
+ keyid = 'player%d' % id
+ if local == 'l' and d.has_key(keyid):
+ namemsg = namemsg + message(
+ CMSG_PLAYER_NAME, id, d[keyid])
+ if namemsg:
+ self.s.sendall(namemsg)
+
+ def msg_ping(self, *rest):
+ self.s.sendall(message(CMSG_PONG, *rest))
+ self.checkcfgfile()
+ if rest and self.udpsock_low is not None:
+ udpkbytes = rest[0]
+ if not udpkbytes:
+ return
+ #inp = self.udpbytecounter / (udpkbytes*1024.0)
+ #print "(%d%% packet loss)" % int(100*(1.0-inp))
+ if (udpkbytes<<10) * UDP_EXPECTED_RATIO > self.udpbytecounter:
+ # too many packets were dropped (including, maybe, all of them)
+ self.udpsock_low += 1
+ if self.udpsock_low >= 3 and self.initlevel >= 1:
+ # third time now -- that's too much
+ print "Note: routing UDP traffic over TCP",
+ inp = self.udpbytecounter / (udpkbytes*1024.0)
+ print "(%d%% packet loss)" % int(100*(1.0-inp))
+ self.start_udp_over_tcp()
+ self.s.sendall(message(CMSG_UDP_PORT, MSG_INLINE_FRAME))
+ else:
+ # enough packets received
+ self.udpsock_low = 0
+
+ def msg_pong(self, *rest):
+ if self.initlevel == 0:
+ self.startplaying()
+ self.initlevel = 1
+ elif self.initlevel == 1:
+ if self.snd and self.snd.has_music:
+ self.s.sendall(message(CMSG_ENABLE_MUSIC, 2))
+ self.initlevel = 2
+ if not self.taskbarfree and not self.taskbarmode:
+ self.taskbarfree = 1
+ self.settaskbar(1)
+
+ def msg_inline_frame(self, data, *rest):
+ if self.pending_udp_data is not None:
+ self.pending_udp_data = self.udp_over_tcp_decompress(data)
+
+ MESSAGES = {
+ MSG_BROADCAST_PORT:msg_broadcast_port,
+ MSG_DEF_PLAYFIELD: msg_def_playfield,
+ MSG_DEF_KEY : msg_def_key,
+ MSG_DEF_ICON : msg_def_icon,
+ MSG_DEF_BITMAP : msg_def_bitmap,
+ MSG_DEF_SAMPLE : msg_def_sample,
+ MSG_PLAY_MUSIC : msg_play_music,
+ MSG_FADEOUT : msg_fadeout,
+ MSG_PLAYER_JOIN : msg_player_join,
+ MSG_PLAYER_KILL : msg_player_kill,
+ MSG_PLAYER_ICON : msg_player_icon,
+ MSG_PING : msg_ping,
+ MSG_PONG : msg_pong,
+ MSG_INLINE_FRAME : msg_inline_frame,
+ MSG_PATCH_FILE : msg_patch_file,
+ MSG_ZPATCH_FILE : msg_zpatch_file,
+ MSG_MD5_FILE : msg_md5_file,
+## MSG_LOAD_PREFIX : msg_load_prefix,
+ }
+
+
+def run(s, sockaddr, *args, **kw):
+ try:
+ import psyco
+ except ImportError:
+ pass
+ else:
+ psyco.bind(Playfield.update_sprites)
+ Playfield(s, sockaddr).run(*args, **kw)
diff --git a/display/playback.py b/display/playback.py
new file mode 100644
index 0000000..f0e698b
--- /dev/null
+++ b/display/playback.py
@@ -0,0 +1,203 @@
+#! /usr/bin/env python
+
+import sys, os, gzip
+from socket import *
+from select import select
+import cStringIO, struct, zlib
+import time
+sys.path.insert(0, os.pardir)
+from common.msgstruct import *
+from common import hostchooser
+import modes
+from modes import KeyPressed, KeyReleased
+
+#import psyco; psyco.full()
+
+SOURCEDIR = os.pardir
+
+
+def loadpixmap(dpy, data, colorkey=None):
+ f = cStringIO.StringIO(data)
+ sig = f.readline().strip()
+ assert sig == "P6"
+ while 1:
+ line = f.readline().strip()
+ if not line.startswith('#'):
+ break
+ wh = line.split()
+ w, h = map(int, wh)
+ sig = f.readline().strip()
+ assert sig == "255"
+ data = f.read()
+ f.close()
+ if colorkey is None:
+ colorkey = -1
+ elif colorkey < 0:
+ r, g, b = struct.unpack("BBB", self.data[:3])
+ colorkey = b | (g<<8) | (r<<16)
+ return dpy.pixmap(w, h, data, colorkey)
+
+class Icon:
+ def __init__(self, bitmap, (x, y, w, h), alpha):
+ self.rect = x, y, w, h
+ self.size = w, h
+ self.bitmap = bitmap
+ self.alpha = alpha
+
+
+class Playback:
+ gameident = 'Playback'
+
+ def __init__(self, filename, mode=('x', 'off', {})):
+ f = gzip.open(filename, 'rb')
+ self.screenmode = mode
+ self.width = None
+ self.deffiles = {}
+ self.defbitmaps = {}
+ self.deficons = {}
+ self.icons = {}
+ self.frames = []
+ inbuf = ''
+ while 1:
+ values, inbuf = decodemessage(inbuf)
+ if not values:
+ # incomplete message
+ data = f.read(8192)
+ if not data:
+ break
+ inbuf += data
+ else:
+ #print values[0],
+ fn = Playback.MESSAGES.get(values[0], self.msg_unknown)
+ fn(self, *values[1:])
+ print '%d frames in file.' % len(self.frames)
+ f.close()
+ assert self.width, "no playfield definition found in file"
+
+ self.dpy = modes.open_dpy(self.screenmode,
+ self.width, self.height, self.gameident)
+ self.dpy.clear() # backcolor is ignored
+ self.sprites = []
+ self.buildicons()
+ self.go(0)
+
+ def buildicons(self):
+ bitmaps = {}
+ for bmpcode, (data, colorkey) in self.defbitmaps.items():
+ if isinstance(data, str):
+ data = zlib.decompress(data)
+ else:
+ data = self.deffiles[data]
+ bitmaps[bmpcode] = loadpixmap(self.dpy, data, colorkey)
+ for icocode, (bmpcode, rect, alpha) in self.deficons.items():
+ self.icons[icocode] = Icon(bitmaps[bmpcode], rect, alpha)
+
+ def go(self, n):
+ self.n = n
+ self.update_sprites(self.frames[n])
+ self.dpy.flip()
+
+ def save(self, filename=None):
+ "shm only!"
+ w, h, data, reserved = self.dpy.getppm((0, 0, self.width, self.height))
+ f = open(filename or ('frame%d.ppm' % self.n), 'wb')
+ print >> f, 'P6'
+ print >> f, w, h
+ print >> f, 255
+ for i in range(0, len(data), 4):
+ f.write(data[i+2]+data[i+1]+data[i])
+ f.close()
+
+ def update_sprites(self, udpdata):
+ sprites = self.sprites
+ unpack = struct.unpack
+ base = 0
+ for j in range(len(sprites)):
+ if sprites[j][0] != udpdata[base:base+6]:
+ removes = sprites[j:]
+ del sprites[j:]
+ removes.reverse()
+ eraser = self.dpy.putppm
+ for reserved, eraseargs in removes:
+ eraser(*eraseargs)
+ break
+ base += 6
+ try:
+ overlayer = self.dpy.overlayppm
+ except AttributeError:
+ getter = self.dpy.getppm
+ setter = self.dpy.putppm
+ #print "%d sprites redrawn" % (len(udpdata)/6-j)
+ for j in range(base, len(udpdata)-5, 6):
+ info = udpdata[j:j+6]
+ x, y, icocode = unpack("!hhh", info[:6])
+ try:
+ ico = self.icons[icocode]
+ sprites.append((info, (x, y, getter((x, y) + ico.size))))
+ setter(x, y, ico.bitmap, ico.rect)
+ except KeyError:
+ #print "bad ico code", icocode
+ pass # ignore sprites with bad ico (probably not defined yet)
+ else:
+ for j in range(base, len(udpdata)-5, 6):
+ info = udpdata[j:j+6]
+ x, y, icocode = unpack("!hhh", info[:6])
+ try:
+ ico = self.icons[icocode]
+ overlay = overlayer(x, y, ico.bitmap, ico.rect, ico.alpha)
+ sprites.append((info, overlay))
+ except KeyError:
+ #print "bad ico code", icocode
+ pass # ignore sprites with bad ico (probably not defined yet)
+
+ def msg_unknown(self, *rest):
+ pass
+
+ def msg_patch_file(self, fileid, position, data, lendata=None, *rest):
+ try:
+ s = self.deffiles[fileid]
+ except KeyError:
+ s = ''
+ if len(s) < position:
+ s += '\x00' * (position-len(s))
+ s = s[:position] + data + s[position+len(s):]
+ self.deffiles[fileid] = s
+
+ def msg_zpatch_file(self, fileid, position, data, *rest):
+ data1 = zlib.decompress(data)
+ self.msg_patch_file(fileid, position, data1, len(data), *rest)
+
+ def msg_md5_file(self, fileid, filename, position, length, checksum, *rest):
+ fn = os.path.join(SOURCEDIR, filename)
+ f = open(fn, 'rb')
+ f.seek(position)
+ data = f.read(length)
+ f.close()
+ assert len(data) == length
+ self.msg_patch_file(fileid, position, data)
+
+ def msg_def_playfield(self, width, height, *rest):
+ self.width, self.height = width, height
+
+ def msg_def_icon(self, bmpcode, icocode, x, y, w, h, alpha=255, *rest):
+ self.deficons[icocode] = bmpcode, (x, y, w, h), alpha
+
+ def msg_def_bitmap(self, bmpcode, data, colorkey=None, *rest):
+ self.defbitmaps[bmpcode] = data, colorkey
+
+ def msg_recorded(self, data):
+ self.frames.append(data)
+
+ MESSAGES = {
+ MSG_PATCH_FILE : msg_patch_file,
+ MSG_ZPATCH_FILE : msg_zpatch_file,
+ MSG_MD5_FILE : msg_md5_file,
+ MSG_DEF_PLAYFIELD: msg_def_playfield,
+ MSG_DEF_ICON : msg_def_icon,
+ MSG_DEF_BITMAP : msg_def_bitmap,
+ MSG_RECORDED : msg_recorded,
+ }
+
+
+if __name__ == '__main__' and len(sys.argv) > 1:
+ p = Playback(sys.argv[1])
diff --git a/display/puremixer.py b/display/puremixer.py
new file mode 100644
index 0000000..d0b567a
--- /dev/null
+++ b/display/puremixer.py
@@ -0,0 +1,123 @@
+import sys, audioop
+
+
+class PureMixer:
+ #
+ # An audio mixer in Python based on audioop
+ #
+ # Note that opening the audio device itself is outside the scope of
+ # this module. Anything else could also be done with the mixed data,
+ # e.g. stored on disk, for all this module knows.
+
+ def __init__(self, freq=44100, bits=8, signed=0,
+ channels=1, byteorder=None):
+ """Open the mixer and set its parameters."""
+ self.freq = freq
+ self.bytes = bits/8
+ self.signed = signed
+ self.channels = channels
+ self.byteorder = byteorder or sys.byteorder
+ self.parameters = (freq, self.bytes, signed, channels, self.byteorder)
+ self.bytespersample = channels*self.bytes
+ self.queue = '\x00' * self.bytes
+
+ def resample(self, data, freq=44100, bits=8, signed=0,
+ channels=1, byteorder=None):
+ "Convert a sample to the mixer's own format."
+ bytes = bits/8
+ byteorder = byteorder or sys.byteorder
+ if (freq, bytes, signed, channels, byteorder) == self.parameters:
+ return data
+ # convert to native endianness
+ if byteorder != sys.byteorder:
+ data = byteswap(data, bytes)
+ byteorder = sys.byteorder
+ # convert unsigned -> signed for the next operations
+ if not signed:
+ data = audioop.bias(data, bytes, -(1<<(bytes*8-1)))
+ signed = 1
+ # convert stereo -> mono
+ while channels > self.channels:
+ assert channels % 2 == 0
+ data = audioop.tomono(data, bytes, 0.5, 0.5)
+ channels /= 2
+ # resample to self.freq
+ if freq != self.freq:
+ data, ignored = audioop.ratecv(data, bytes, channels,
+ freq, self.freq, None)
+ freq = self.freq
+ # convert between 8bits and 16bits
+ if bytes != self.bytes:
+ data = audioop.lin2lin(data, bytes, self.bytes)
+ bytes = self.bytes
+ # convert mono -> stereo
+ while channels < self.channels:
+ data = audioop.tostereo(data, bytes, 1.0, 1.0)
+ channels *= 2
+ # convert signed -> unsigned
+ if not self.signed:
+ data = audioop.bias(data, bytes, 1<<(bytes*8-1))
+ signed = 0
+ # convert to mixer endianness
+ if byteorder != self.byteorder:
+ data = byteswap(data, bytes)
+ byteorder = self.byteorder
+ # done
+ if (freq, bytes, signed, channels, byteorder) != self.parameters:
+ raise ValueError, 'sound sample conversion failed'
+ return data
+
+ def wavesample(self, file):
+ "Read a sample from a .wav file (or file-like object)."
+ import wave
+ w = wave.open(file, 'r')
+ return self.resample(w.readframes(w.getnframes()),
+ freq = w.getframerate(),
+ bits = w.getsampwidth() * 8,
+ signed = w.getsampwidth() > 1,
+ channels = w.getnchannels(),
+ byteorder = 'little')
+
+ def mix(self, mixer_channels, bufsize):
+ """Mix the next batch buffer.
+ Each object in the mixer_channels list must be a file-like object
+ with a 'read(size)' method."""
+ data = ''
+ already_seen = {}
+ channels = mixer_channels[:]
+ channels.reverse()
+ for c in channels:
+ if already_seen.has_key(c):
+ data1 = ''
+ else:
+ data1 = c.read(bufsize)
+ already_seen[c] = 1
+ if data1:
+ l = min(len(data), len(data1))
+ data = (audioop.add(data[:l], data1[:l], 1) +
+ (data1[l:] or data[l:]))
+ else:
+ try:
+ mixer_channels.remove(c)
+ except ValueError:
+ pass
+ data += self.queue * ((bufsize - len(data)) / self.bytes)
+ self.queue = data[-self.bytes:]
+ return data
+
+
+def byteswap(data, byte):
+ if byte == 1:
+ return
+ if byte == 2:
+ typecode = 'h'
+ elif byte == 4:
+ typecode = 'i'
+ else:
+ raise ValueError, 'cannot convert endianness for samples of %d bytes' % byte
+ import array
+ a = array.array(typecode, data)
+ if a.itemsize != byte:
+ raise ValueError, 'endianness convertion failed'
+ a.byteswap()
+ return a.tostring()
diff --git a/display/pythonxlibintf.py b/display/pythonxlibintf.py
new file mode 100644
index 0000000..5e32715
--- /dev/null
+++ b/display/pythonxlibintf.py
@@ -0,0 +1,202 @@
+
+ ################################################
+## pygame-based implementation of xshm ##
+################################################
+
+import os, sys
+from Xlib import X, display
+
+
+# -*-*- SLOOWWW -*-*-
+import psyco; psyco.full()
+
+
+class Display:
+
+ def __init__(self, width, height, title):
+ self.dpy = display.Display()
+ self.default_scr = self.dpy.screen()
+ self.root = self.default_scr.root
+ self.width = width
+ self.height = height
+ self.depth = self.default_scr.root_depth
+
+ self.backpixmap = self.root.create_pixmap(width, height, self.depth)
+ self.win = self.root.create_window(
+ 0, 0, width, height, 0, self.depth,
+ override_redirec = 0,
+ background_pixel = self.default_scr.black_pixel,
+ backing_store = X.NotUseful,
+ )
+ self.win.map()
+
+ self.gc = self.win.create_gc()
+ self.gc_and = self.win.create_gc()
+ self.gc_or = self.win.create_gc()
+
+ self.gc.change(foreground = self.default_scr.black_pixel)
+ self.gc_and.change(function = X.GXand)
+ self.gc_or .change(function = X.GXor)
+
+ self.selectinput = 0
+ self.keyev = []
+ self.mouseev = []
+ self.motionev = None
+ self.dpy.flush()
+
+ pixel = "\x00\x00\x80"
+ hole = "\x01\x01\x01"
+ self.taskbkgnd = self.pixmap(32, 32,
+ ((pixel+hole)*16 + (hole+pixel)*16) * 16,
+ 0x010101)
+
+ def pixmap(self, w, h, data, colorkey=-1):
+ print >> sys.stderr, '.',
+ extent = w*h
+ depth = self.depth
+ if depth >= 24:
+ bitmap_pad = 32
+ else:
+ bitmap_pad = 16
+ scanline = ((w+bitmap_pad-1) & ~(bitmap_pad-1)) / 8;
+ if colorkey >= 0:
+ key = (chr(colorkey >> 16) +
+ chr((colorkey>>8) & 0xFF) +
+ chr(colorkey & 0xFF))
+ else:
+ key = None
+ if depth == 15:
+ p_size = 5, 5, 5
+ elif depth == 16:
+ p_size = 5, 6, 5
+ elif depth == 24 or depth == 32:
+ p_size = 8, 8, 8
+ else:
+ raise ValueError, 'unsupported screen depth %d' % depth
+
+ imgdata = []
+ maskdata = []
+
+ for color in range(3):
+ plane = 128
+ while plane >= (1<<(8-p_size[color])):
+ src = 0
+ for y in range(h):
+ imgline = 0L
+ maskline = 0L
+ shifter = 1L
+ for x in range(w):
+ if data[src:src+3] == key:
+ # transparent
+ maskline |= shifter
+ elif ord(data[src+color]) & plane:
+ imgline |= shifter
+ shifter <<= 1
+ src += 3
+ imgdata.append(long2string(imgline, scanline))
+ maskdata.append(long2string(maskline, scanline))
+ plane /= 2
+
+ imgdata = ''.join(imgdata)
+ if colorkey >= 0:
+ maskdata = ''.join(maskdata)
+ mask = self.win.create_pixmap(w, h, depth)
+ mask.put_image(self.gc, 0, 0, w, h, X.XYPixmap, depth, 0, maskdata)
+ else:
+ mask = None
+ imgdata = ''.join(imgdata)
+ image = self.win.create_pixmap(w, h, depth)
+ image.put_image(self.gc, 0, 0, w, h, X.XYPixmap, depth, 0, imgdata)
+ image.mask = mask
+ image.size = w, h
+ return image
+
+ def getppm(self, (x, y, w, h), bkgnd=None):
+ if bkgnd is None:
+ bkgnd = self.win.create_pixmap(w, h, self.depth)
+ bkgnd.mask = None
+ bkgnd.size = w, h
+ bkgnd.copy_area(self.gc, self.backpixmap, x, y, w, h, 0, 0)
+ return bkgnd
+
+ def putppm(self, x, y, image, rect=None):
+ if rect:
+ x1, y1, w1, h1 = rect
+ else:
+ x1 = y1 = 0
+ w1, h1 = image.size
+ if image.mask is None:
+ self.backpixmap.copy_area(self.gc, image, x1, y1, w1, h1, x, y)
+ else:
+ self.backpixmap.copy_area(self.gc_and, image.mask,
+ x1, y1, w1, h1, x, y)
+ self.backpixmap.copy_area(self.gc_or, image,
+ x1, y1, w1, h1, x, y)
+
+ def flip(self):
+ self.win.copy_area(self.gc, self.backpixmap,
+ 0, 0, self.width, self.height, 0, 0)
+ self.dpy.flush()
+ self.readXevents()
+
+ def close(self):
+ self.dpy.close()
+
+ def clear(self):
+ self.backpixmap.fill_rectangle(self.gc, 0, 0, self.width, self.height)
+
+ def readXevents(self):
+ n = self.dpy.pending_events()
+ if n:
+ for i in range(n):
+ event = self.dpy.next_event()
+ if event.type == X.KeyPress or event.type == X.KeyRelease:
+ self.keyev.append((event.detail, event.type))
+ elif event.type == X.ButtonPress:
+ self.mouseev.append((event.event_x, event.event_y))
+ elif event.type == X.MotionNotify:
+ self.motionev = event.event_x, event.event_y
+ elif event.type == X.DestroyNotify:
+ raise SystemExit
+ self.readXevents()
+
+ def enable_event(self, mask):
+ self.selectinput |= mask
+ self.win.change_attributes(event_mask=self.selectinput)
+
+ def keyevents(self):
+ if not (self.selectinput & X.KeyReleaseMask):
+ self.enable_event(X.KeyPressMask | X.KeyReleaseMask)
+ self.readXevents()
+ result = self.keyev
+ self.keyev = []
+ return result
+
+ def mouseevents(self):
+ if not (self.selectinput & X.ButtonPressMask):
+ self.enable_event(X.ButtonPressMask)
+ result = self.mouseev
+ self.mouseev = []
+ return result
+
+ def pointermotion(self):
+ result = self.motionev
+ self.motionev = None
+ return result
+
+ def has_sound(self):
+ return 0
+
+ def selectlist(self):
+ from socket import fromfd, AF_INET, SOCK_STREAM
+ return [fromfd(self.dpy.fileno(), AF_INET, SOCK_STREAM)]
+
+ def taskbar(self, (x, y, w, h)):
+ for j in range(y, y+h, 32):
+ for i in range(x, x+w, 32):
+ self.putppm(i, j, self.taskbkgnd,
+ (0, 0, x+w-i, y+h-j))
+
+
+def long2string(bits, strlen):
+ return ''.join([chr((bits>>n)&0xFF) for n in range(0, 8*strlen, 8)])
diff --git a/display/setup.py b/display/setup.py
new file mode 100755
index 0000000..4776e79
--- /dev/null
+++ b/display/setup.py
@@ -0,0 +1,16 @@
+#! /usr/bin/env python
+
+from distutils.core import setup
+from distutils.extension import Extension
+
+setup ( name="xshm",
+ version="0.2",
+ description="X window system Shared Memory extension",
+ author="Armin & Odie",
+ author_email="arigo@tunes.org",
+ ext_modules=[Extension(name = 'xshm',
+ sources = ['xshm.c'],
+ include_dirs = ['/usr/X11R6/include'],
+ library_dirs = ['/usr/X11R6/lib'],
+ libraries = ['X11', 'Xext'])]
+ )
diff --git a/display/snd_linux.py b/display/snd_linux.py
new file mode 100644
index 0000000..dae82e0
--- /dev/null
+++ b/display/snd_linux.py
@@ -0,0 +1,148 @@
+import sys
+from cStringIO import StringIO
+import puremixer
+from music1 import Music
+
+
+class Sound:
+ # Mono only
+ has_sound = has_music = 0 # until initialized
+
+ BUFFERTIME = 0.09
+ FLOPTIME = 0.07
+
+ Formats = [
+ ('U8', 8, 0, None),
+ ('S8', 8, 1, None),
+ ('S16_NE', 16, 1, None),
+ ('S16_LE', 16, 1, 'little'),
+ ('S16_BE', 16, 1, 'big'),
+ ('U16_LE', 16, 0, 'little'),
+ ('U16_BE', 16, 0, 'big'),
+ ]
+
+ def __init__(self, freq=44100, fmt='S16_NE'):
+ self.f = None
+ self.freq = int(freq)
+ self.format = fmt.upper()
+ self.params = p = {}
+ for name, p['bits'], p['signed'], p['byteorder'] in self.Formats:
+ if name == self.format:
+ break
+ else:
+ print >> sys.stderr, 'available sound formats:'
+ for name, bits, signed, byteorder in self.Formats:
+ print >> sys.stderr, ' %-8s %s' % (name, nicefmttext(
+ bits, signed, byteorder))
+ sys.exit(2)
+
+ import linuxaudiodev
+ try:
+ f = linuxaudiodev.open('w')
+ f.setparameters(self.freq, p['bits'], 1,
+ getattr(linuxaudiodev, 'AFMT_' + self.format))
+ except Exception, e:
+ print >> sys.stderr, "sound disabled: %s: %s" % (
+ e.__class__.__name__, e)
+ return
+ self.f = f
+ self.mixer = mixer = puremixer.PureMixer(**p)
+ buffertime = self.BUFFERTIME
+ self.bufsize = int(mixer.bytespersample*mixer.freq*buffertime +
+ 255.5) & ~ 255
+ if self.bufsize > f.bufsize():
+ self.bufsize = f.bufsize()
+ buffertime = self.bufsize / float(freq)
+ self.buffertime = buffertime
+ self.mixer_channels = []
+ self.mixer_accum = {}
+ self.has_sound = 1
+ self.has_music = 1
+
+ def close(self):
+ self.f.close()
+ self.f = None
+
+ def sound(self, f):
+ return self.mixer.wavesample(f.fopen())
+
+ def flop(self):
+ self.mixer_accum = {}
+ if self.f is None:
+ return
+ for i in range(3):
+ bufsize = self.bufsize - self.f.obufcount()
+ if bufsize <= 0:
+ break
+ self.f.write(self.mixer.mix(self.mixer_channels, bufsize))
+ #cnt = getattr(self, 'CNT', 0)
+ #import time
+ #print cnt, time.time()
+ #self.CNT = cnt+1
+ return self.FLOPTIME
+
+ def play(self, sound, lvolume, rvolume):
+ # volume ignored
+ if sound not in self.mixer_accum:
+ self.mixer_channels.append(StringIO(sound))
+ self.mixer_accum[sound] = 1
+
+ def play_musics(self, musics, loop_from):
+ self.cmusics = musics, loop_from, -1
+ if self.mixer_channels[:1] != [self]:
+ self.mixer_channels.insert(0, self)
+
+ def read(self, size):
+ "Provide some more data to self.mixer.poll()."
+ musics, loop_from, c = self.cmusics
+ if c < 0:
+ data = ''
+ else:
+ data = musics[c].mixed.decode(self.mixer, size)
+ if not data:
+ c += 1
+ if c >= len(musics): # end
+ c = loop_from
+ if c >= len(musics):
+ return ''
+ self.cmusics = musics, loop_from, c
+ try:
+ mixed = musics[c].mixed
+ except AttributeError:
+ mixed = musics[c].mixed = Music(musics[c].freezefilename())
+ mixed.openchannel()
+ data = mixed.decode(self.mixer, size)
+ if 0 < len(data) < size:
+ data += self.read(size - len(data))
+ return data
+
+ def fadeout(self, millisec):
+ self.cmusics = [], 0, -1
+
+
+def imperror():
+ try:
+ import linuxaudiodev
+ except ImportError:
+ if sys.platform.startswith('linux'):
+ return 'linuxaudiodev module not installed'
+ else:
+ return 'only available on Linux'
+
+def nicefmttext(bits, signed, byteorder):
+ s = '%s %d bits' % (signed and 'signed' or 'unsigned', bits)
+ if byteorder:
+ s += ' %s endian' % byteorder
+ return s
+
+def htmloptionstext(nameval):
+ import modes
+ l = ['<font size=-1>Sampling <%s>' % nameval('select', 'fmt')]
+ for name, bits, signed, byteorder in Sound.Formats:
+ l.append('<'+nameval('option', 'fmt', name, default='S16_NE')+'>'+
+ nicefmttext(bits, signed, byteorder))
+ l+= ['</select> rate ',
+ '<%s size=5>Hz</font>' % nameval('text', 'freq', default='44100'),
+ '<br>',
+ modes.musichtmloptiontext(nameval)]
+ return '\n'.join(l)
diff --git a/display/snd_off.py b/display/snd_off.py
new file mode 100644
index 0000000..9d2e640
--- /dev/null
+++ b/display/snd_off.py
@@ -0,0 +1,5 @@
+class Sound:
+ has_sound = 0 # no sound
+ has_music = 0 # no music
+ def __init__(self):
+ pass
diff --git a/display/snd_pygame.py b/display/snd_pygame.py
new file mode 100644
index 0000000..e1a1455
--- /dev/null
+++ b/display/snd_pygame.py
@@ -0,0 +1,82 @@
+import sys
+from modes import musichtmloptiontext as htmloptionstext
+from pygame.locals import *
+import pygame.mixer
+
+if pygame.mixer is None:
+ raise ImportError
+
+
+#ENDMUSICEVENT = USEREVENT
+
+
+class Sound:
+ has_sound = has_music = 0 # until initialized
+
+ def __init__(self):
+ try:
+ pygame.mixer.init()
+ except pygame.error, e:
+ print >> sys.stderr, "sound disabled: %s" % str(e)
+ else:
+ self.has_sound = 1
+ try:
+ from pygame.mixer import music
+ except ImportError:
+ pass
+ else:
+ self.has_music = music is not None
+ self.cmusics = None
+
+ def close(self):
+ try:
+ pygame.mixer.stop()
+ except pygame.error:
+ pass
+ if self.has_music:
+ try:
+ pygame.mixer.music.stop()
+ except pygame.error:
+ pass
+
+ def sound(self, f):
+ return pygame.mixer.Sound(f.freezefilename())
+
+ def flop(self):
+ # the events are not processed if pygame is not also the display,
+ # so ENDMUSICEVENT will not arrive -- poll for end of music
+ if self.cmusics and not pygame.mixer.music.get_busy():
+ self.next_music()
+
+ def play(self, sound, lvolume, rvolume):
+ channel = pygame.mixer.find_channel(1)
+ channel.stop()
+ try:
+ channel.set_volume(lvolume, rvolume)
+ except TypeError:
+ channel.set_volume(0.5 * (lvolume+rvolume))
+ channel.play(sound)
+
+ def play_musics(self, musics, loop_from):
+ #dpy_pygame.EVENT_HANDLERS[ENDMUSICEVENT] = self.next_music
+ #pygame.mixer.music.set_endevent(ENDMUSICEVENT)
+ self.cmusics = musics, loop_from, 0
+ self.next_music()
+
+ def next_music(self, e=None):
+ if self.cmusics:
+ musics, loop_from, c = self.cmusics
+ if c >= len(musics): # end
+ c = loop_from
+ if c >= len(musics):
+ pygame.mixer.music.stop()
+ self.cmusics = None
+ return
+ pygame.mixer.music.load(musics[c].freezefilename())
+ pygame.mixer.music.play()
+ self.cmusics = musics, loop_from, c+1
+
+ def fadeout(self, millisec):
+ #print "fadeout:", millisec
+ pygame.mixer.music.fadeout(millisec)
+ self.cmusics = None
diff --git a/display/snd_windows.py b/display/snd_windows.py
new file mode 100644
index 0000000..3aea3bd
--- /dev/null
+++ b/display/snd_windows.py
@@ -0,0 +1,93 @@
+import sys
+from cStringIO import StringIO
+import puremixer
+import wingame
+from music1 import Music
+
+
+class Sound:
+ # Mono only
+ has_sound = has_music = 0 # until initialized
+
+ BUFFERTIME = 0.09
+ FLOPTIME = 0.07
+
+ def __init__(self, freq=44100, bits=16):
+ self.freq = int(freq)
+ self.bits = int(bits)
+ self.bufsize = (int(self.BUFFERTIME*self.freq*self.bits/8) + 64) & ~63
+
+ try:
+ self.audio = wingame.Audio(1, self.freq, self.bits, self.bufsize)
+ except Exception, e:
+ print >> sys.stderr, "sound disabled: %s: %s" % (
+ e.__class__.__name__, e)
+ return
+ self.mixer = puremixer.PureMixer(self.freq, self.bits, self.bits==16,
+ byteorder='little')
+ self.mixer_channels = []
+ self.mixer_accum = {}
+ self.has_sound = 1
+ self.has_music = 1
+
+ def stop(self):
+ self.audio.close()
+
+ def sound(self, f):
+ return self.mixer.wavesample(f.fopen())
+
+ def flop(self):
+ self.mixer_accum = {}
+ while self.audio.ready():
+ self.audio.write(self.mixer.mix(self.mixer_channels, self.bufsize))
+ return self.FLOPTIME
+
+ def play(self, sound, lvolume, rvolume):
+ # volume ignored
+ if sound not in self.mixer_accum:
+ self.mixer_channels.append(StringIO(sound))
+ self.mixer_accum[sound] = 1
+
+ def play_musics(self, musics, loop_from):
+ self.cmusics = musics, loop_from, -1
+ self.mixer_channels.insert(0, self)
+
+ def read(self, size):
+ "Provide some more data to self.mixer.poll()."
+ musics, loop_from, c = self.cmusics
+ if c < 0:
+ data = ''
+ else:
+ data = musics[c].mixed.decode(self.mixer, size)
+ if not data:
+ c += 1
+ if c >= len(musics): # end
+ c = loop_from
+ if c >= len(musics):
+ return ''
+ self.cmusics = musics, loop_from, c
+ try:
+ mixed = musics[c].mixed
+ except AttributeError:
+ mixed = musics[c].mixed = Music(musics[c].freezefilename())
+ mixed.openchannel()
+ data = mixed.decode(self.mixer, size)
+ if 0 < len(data) < size:
+ data += self.read(size - len(data))
+ return data
+
+ def fadeout(self, millisec):
+ self.cmusics = [], 0, -1
+
+
+def htmloptionstext(nameval):
+ import modes
+ l = ['<font size=-1>Sampling <%s>' % nameval('select', 'bits')]
+ for bits in (8, 16):
+ l.append('<'+nameval('option', 'bits', str(bits), default='16')+'>'+
+ '%d bits' % bits)
+ l+= ['</select> rate ',
+ '<%s size=5>Hz</font>' % nameval('text', 'freq', default='44100'),
+ '<br>',
+ modes.musichtmloptiontext(nameval)]
+ return '\n'.join(l)
diff --git a/display/windows/.cvsignore b/display/windows/.cvsignore
new file mode 100644
index 0000000..0e01180
--- /dev/null
+++ b/display/windows/.cvsignore
@@ -0,0 +1,3 @@
+*.ncb
+*.opt
+*.plg
diff --git a/display/windows/wingame.c b/display/windows/wingame.c
new file mode 100755
index 0000000..644e496
--- /dev/null
+++ b/display/windows/wingame.c
@@ -0,0 +1,867 @@
+#include <Python.h>
+#include <windows.h>
+#include <mmsystem.h>
+
+
+ /************************** 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; i<pal.palNumEntries; i++) {
+ self->screenbmpinfo.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; y<h; y++ )
+ for( x=0; x<w; x++, input+=3, dst += bytes_per_pixel )
+ {
+ int r = input[0];
+ int g = input[1];
+ int b = input[2];
+ if( ((r<<16)|(g<<8)|b) == keycol )
+ for( b=0; b<bytes_per_pixel; b++ )
+ dst[b] = ((unsigned char *)&packed_keycol)[b];
+ else
+ pack_pixel(self, dst, r, g, b, self->bpp);
+ }
+ 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<clipx) { src+=(clipx-x)*bytes_per_pixel; w+=x-clipx; x=clipx; }
+ if (y<clipy) { src+=(clipy-y)*scanline; h+=y-clipy; y=clipy; }
+ if (x+w > 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; i<w; i++)
+ for (b=0; b<bytes_per_pixel; b++)
+ *data++ = *src++;
+ else
+ {
+ unsigned char *keycol_bytes = (unsigned char *)&keycol;
+ for (i=0; i<w; i++)
+ {
+ int transparent = 1;
+ for( b=0; b<bytes_per_pixel; b++ )
+ transparent = transparent && (keycol_bytes[b] == src[b]);
+
+ if (!transparent)
+ for( b=0; b<bytes_per_pixel; b++ )
+ *data++ = *src++;
+ else
+ {
+ data += bytes_per_pixel;
+ src += bytes_per_pixel;
+ }
+ }
+ }
+ src = src0 + scanline;
+ data = data0 + self->screenscanline;
+ 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<clipx) { dst+=(clipx-x)*bytes_per_pixel; w+=x-clipx; x=clipx; }
+ if (y<clipy) { dst+=(clipy-y)*scanline; h+=y-clipy; y=clipy; }
+ if (x+w > 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; i<w; i++)
+ {
+ for( b=0; b<bytes_per_pixel; b++ )
+ *dst++ = *data++;
+ }
+ dst = dst0 + scanline;
+ data = data0 + self->screenscanline;
+ 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);
+}
diff --git a/display/windows/wingame.def b/display/windows/wingame.def
new file mode 100755
index 0000000..de59dda
--- /dev/null
+++ b/display/windows/wingame.def
@@ -0,0 +1,2 @@
+EXPORTS
+ initwingame
diff --git a/display/windows/wingame.dsp b/display/windows/wingame.dsp
new file mode 100755
index 0000000..ce1db26
--- /dev/null
+++ b/display/windows/wingame.dsp
@@ -0,0 +1,111 @@
+# Microsoft Developer Studio Project File - Name="wingame" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** NICHT BEARBEITEN **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=wingame - Win32 Debug
+!MESSAGE Dies ist kein gültiges Makefile. Zum Erstellen dieses Projekts mit NMAKE
+!MESSAGE verwenden Sie den Befehl "Makefile exportieren" und führen Sie den Befehl
+!MESSAGE
+!MESSAGE NMAKE /f "wingame.mak".
+!MESSAGE
+!MESSAGE Sie können beim Ausführen von NMAKE eine Konfiguration angeben
+!MESSAGE durch Definieren des Makros CFG in der Befehlszeile. Zum Beispiel:
+!MESSAGE
+!MESSAGE NMAKE /f "wingame.mak" CFG="wingame - Win32 Debug"
+!MESSAGE
+!MESSAGE Für die Konfiguration stehen zur Auswahl:
+!MESSAGE
+!MESSAGE "wingame - Win32 Release" (basierend auf "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "wingame - Win32 Debug" (basierend auf "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "wingame - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WINGAME_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "\home\python222\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WINGAME_EXPORTS" /U "_DEBUG" /U "DEBUG" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x100c /d "NDEBUG"
+# ADD RSC /l 0x100c /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib /nologo /dll /machine:I386 /out:"../wingame.pyd" /libpath:"\home\python23\libs" /libpath:"\home\python222\libs"
+
+!ELSEIF "$(CFG)" == "wingame - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WINGAME_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "\home\python222\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WINGAME_EXPORTS" /U "_DEBUG" /U "DEBUG" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x100c /d "_DEBUG"
+# ADD RSC /l 0x100c /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib /nologo /dll /debug /machine:I386 /out:"../wingame.pyd" /pdbtype:sept /libpath:"\home\python222\libs"
+
+!ENDIF
+
+# Begin Target
+
+# Name "wingame - Win32 Release"
+# Name "wingame - Win32 Debug"
+# Begin Group "Quellcodedateien"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\wingame.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\wingame.def
+# End Source File
+# End Group
+# Begin Group "Header-Dateien"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# Begin Group "Ressourcendateien"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/display/windows/wingame.dsw b/display/windows/wingame.dsw
new file mode 100755
index 0000000..06f07c9
--- /dev/null
+++ b/display/windows/wingame.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNUNG: DIESE ARBEITSBEREICHSDATEI DARF NICHT BEARBEITET ODER GELÖSCHT WERDEN!
+
+###############################################################################
+
+Project: "wingame"=.\wingame.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/display/xshm.c b/display/xshm.c
new file mode 100644
index 0000000..31ea861
--- /dev/null
+++ b/display/xshm.c
@@ -0,0 +1,1202 @@
+#include <Python.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/XShm.h>
+
+typedef struct {
+ XImage* m_shm_image;
+ XShmSegmentInfo m_shminfo;
+ int m_width, m_height;
+} XImage_Shm;
+
+typedef struct {
+ PyObject_HEAD
+ Display* dpy;
+ int default_scr;
+ Window root, win;
+ int width, height;
+ XVisualInfo visual_info;
+ GC gc, gc_and, gc_or;
+ XImage_Shm plane;
+ Pixmap backpixmap;
+ int shmmode;
+ int selectinput;
+ PyObject* keyevents;
+ PyObject* mouseevents;
+ PyObject* motionevent;
+} DisplayObject;
+
+typedef struct {
+ PyObject_HEAD
+ DisplayObject* dpy;
+ int width, height;
+ Pixmap mask;
+ Pixmap handle;
+} XPixmapObject;
+
+
+#define DisplayObject_Check(v) ((v)->ob_type == &Display_Type)
+staticforward PyTypeObject Display_Type;
+staticforward PyTypeObject XPixmap_Type;
+
+
+static void pixmap_dealloc(XPixmapObject* pm)
+{
+ if (pm->dpy->dpy)
+ {
+ if (pm->mask != (Pixmap) -1)
+ XFreePixmap(pm->dpy->dpy, pm->mask);
+ XFreePixmap(pm->dpy->dpy, pm->handle);
+ }
+ Py_DECREF(pm->dpy);
+ PyObject_Del(pm);
+}
+
+static XPixmapObject* new_pixmap(DisplayObject* self, int w, int h, int withmask)
+{
+ XPixmapObject* pm = PyObject_New(XPixmapObject, &XPixmap_Type);
+ if (pm != NULL)
+ {
+ Py_INCREF(self);
+ pm->dpy = self;
+ pm->width = w;
+ pm->height = h;
+ pm->handle = XCreatePixmap(self->dpy, self->win, w, h,
+ self->visual_info.depth);
+ if (withmask)
+ pm->mask = XCreatePixmap(self->dpy, self->win, w, h,
+ self->visual_info.depth);
+ else
+ pm->mask = (Pixmap) -1;
+ }
+ return pm;
+}
+
+
+static void flush(DisplayObject* self)
+{
+ XSync(self->dpy, False);
+}
+
+static int create_shm_image(DisplayObject* self, XImage_Shm* img,
+ int width, int height)
+{
+ int image_size = 4*width*height;
+
+ if (XShmQueryExtension(self->dpy) == False)
+ /* does we have the extension at all? */
+ return 0;
+
+ img->m_shm_image = XShmCreateImage(
+ self->dpy,
+ self->visual_info.visual,
+ self->visual_info.depth,
+ ZPixmap,
+ NULL,
+ &img->m_shminfo,
+ width,
+ height);
+ if (img->m_shm_image == NULL)
+ return 0;
+ img->m_width = width;
+ img->m_height = height;
+
+ /* Create shared memory segment: */
+ img->m_shminfo.shmid = shmget(IPC_PRIVATE, image_size, IPC_CREAT|0777);
+ if (img->m_shminfo.shmid < 0)
+ return 0;
+
+ /* Get memory address to segment: */
+ img->m_shminfo.shmaddr = (char *) shmat(img->m_shminfo.shmid, 0, 0);
+
+ /* Mark the segment as destroyable (it will be destroyed when this
+ process terminates) */
+ shmctl(img->m_shminfo.shmid, IPC_RMID, NULL);
+
+ /* Tell XServer that it may only read from it and attach to display: */
+ img->m_shminfo.readOnly = True;
+ XShmAttach (self->dpy, &img->m_shminfo);
+
+ /* Fill the XImage struct: */
+ img->m_shm_image->data = img->m_shminfo.shmaddr;
+ return 1;
+}
+
+static PyObject* new_display(PyObject* dummy, PyObject* args)
+{
+ DisplayObject* self;
+ XSetWindowAttributes attr;
+ int width, height, use_shm=1;
+ if (!PyArg_ParseTuple(args, "ii|i", &width, &height, &use_shm))
+ return NULL;
+
+ self = PyObject_New(DisplayObject, &Display_Type);
+ if (self == NULL)
+ return NULL;
+
+ self->dpy = XOpenDisplay(NULL);
+ if (self->dpy == NULL) goto err;
+ self->default_scr = DefaultScreen(self->dpy);
+ self->root = RootWindow(self->dpy, self->default_scr);
+ self->width = width;
+ self->height = height;
+
+ if (!XMatchVisualInfo(self->dpy, self->default_scr,
+ DefaultDepth(self->dpy,self->default_scr), TrueColor,
+ &self->visual_info)) goto err2;
+
+ /* set window attributes */
+ memset(&attr, 0, sizeof(attr));
+ attr.override_redirect = False;
+ attr.background_pixel = BlackPixel(self->dpy, self->default_scr);
+ attr.backing_store = NotUseful;
+
+ /* Create the window */
+ self->win = XCreateWindow(
+ self->dpy,
+ self->root,
+ 0,
+ 0,
+ width,
+ height,
+ 0,
+ CopyFromParent,
+ CopyFromParent,
+ self->visual_info.visual,
+ CWOverrideRedirect | CWBackPixel | CWBackingStore,
+ &attr);
+ if (self->win == (Window) -1) goto err2;
+
+ XMapRaised(self->dpy, self->win);
+
+ self->shmmode = use_shm &&
+ create_shm_image(self, &self->plane, width, height);
+
+ self->gc = XCreateGC(self->dpy, self->win, 0, 0);
+ if (!self->shmmode)
+ {
+ self->backpixmap = XCreatePixmap(self->dpy, self->root,
+ width, height, self->visual_info.depth);
+ if (self->backpixmap == (Pixmap) -1) goto err2;
+
+ self->gc_and = XCreateGC(self->dpy, self->win, 0, 0);
+ self->gc_or = XCreateGC(self->dpy, self->win, 0, 0);
+ XSetForeground(self->dpy, self->gc, attr.background_pixel);
+ XSetFunction(self->dpy, self->gc_and, GXand);
+ XSetFunction(self->dpy, self->gc_or, GXor);
+ }
+
+ self->selectinput = 0;
+ self->keyevents = NULL;
+ self->mouseevents = NULL;
+ self->motionevent = NULL;
+
+ flush(self);
+ return (PyObject*) self;
+
+ err2:
+ XCloseDisplay(self->dpy);
+ err:
+ Py_DECREF(self);
+ PyErr_SetString(PyExc_IOError, "cannot open X11 display");
+ return NULL;
+}
+
+static void display_close(DisplayObject* self)
+{
+ if (self->dpy)
+ {
+ XCloseDisplay(self->dpy);
+ self->dpy = NULL;
+ }
+}
+
+static void display_dealloc(DisplayObject* self)
+{
+ display_close(self);
+ Py_XDECREF(self->keyevents);
+ Py_XDECREF(self->mouseevents);
+ Py_XDECREF(self->motionevent);
+ 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->dpy)
+ return 1;
+ PyErr_SetString(PyExc_IOError, "X11 connexion already closed");
+ return 0;
+}
+
+static unsigned char* get_dpy_data(DisplayObject* self)
+{
+ unsigned char* result;
+ if (!checkopen(self))
+ return NULL;
+ result = (unsigned char*)(self->plane.m_shminfo.shmaddr);
+ if (!result)
+ PyErr_SetString(PyExc_IOError, "X11 SHM failed");
+ return result;
+}
+
+static PyObject* display_clear1(DisplayObject* self, PyObject* args)
+{
+ if (self->shmmode)
+ {
+ unsigned char* data = get_dpy_data(self);
+ if (data == NULL)
+ return NULL;
+ memset(data, 0,
+ ( self->plane.m_shm_image->bits_per_pixel/8
+ *self->width*self->height ) );
+ }
+ else
+ {
+ if (!checkopen(self))
+ return NULL;
+ XFillRectangle(self->dpy, self->backpixmap, self->gc,
+ 0, 0, self->width, self->height);
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+inline void pack_pixel(unsigned char *data, int r, int g, int b,
+ int depth, int bytes_per_pixel)
+{
+ unsigned short pixel = 0;
+ switch( depth )
+ {
+ /* No True color below 15 bits per pixel */
+ 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( bytes_per_pixel == 3 )
+ {
+ data[0] = b;
+ data[1] = g;
+ data[2] = r;
+ break;
+ }
+ /* else it's on 32 bits. Drop into depth of 32. */
+ case 32:
+ *((long *)data) = (r<<16) | (g<<8) | b;
+ break;
+ }
+}
+
+static PyObject* display_pixmap1(DisplayObject* self, PyObject* args)
+{
+ int w,h;
+ unsigned char* input = NULL;
+ int length;
+ long keycol = -1;
+
+ if (!checkopen(self))
+ return NULL;
+ if (!PyArg_ParseTuple(args, "ii|s#l", &w, &h, &input, &length, &keycol))
+ return NULL;
+
+ if (self->shmmode)
+ {
+ int x, y;
+ int bytes_per_pixel = self->plane.m_shm_image->bits_per_pixel/8;
+ int countblocks, countpixels;
+ PyObject* result;
+ PyObject* strblocks;
+ PyObject* strpixels;
+ unsigned int* pblocks;
+ unsigned char* ppixels;
+ unsigned char* input1;
+
+ if (input == NULL)
+ {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+ if (3*w*h != length)
+ {
+ PyErr_SetString(PyExc_ValueError, "bad string length");
+ return NULL;
+ }
+
+ /* Convert the image to our internal format.
+ See display_putppm1() for a description of the format.
+ */
+
+ countblocks = 0;
+ countpixels = 0;
+ input1 = input;
+ for (y=0; y<h; y++)
+ {
+ int opaque = 0;
+ for (x=0; x<w; x++)
+ {
+ unsigned int r = input1[0];
+ unsigned int g = input1[1];
+ unsigned int b = input1[2];
+ input1 += 3;
+ if (((r<<16)|(g<<8)|b) == keycol)
+ opaque = 0;
+ else
+ {
+ if (!opaque)
+ {
+ countblocks++; /* start a new block */
+ opaque = 1;
+ }
+ countpixels++;
+ }
+ }
+ countblocks++; /* end-of-line marker block */
+ }
+
+ /* allocate memory */
+ strblocks = PyString_FromStringAndSize(NULL,
+ countblocks*sizeof(int));
+ if (strblocks == NULL)
+ return NULL;
+ strpixels = PyString_FromStringAndSize(NULL,
+ countpixels*bytes_per_pixel);
+ if (strpixels == NULL)
+ {
+ Py_DECREF(strblocks);
+ return NULL;
+ }
+
+ /* write data */
+ pblocks = (unsigned int*) PyString_AS_STRING(strblocks);
+ ppixels = (unsigned char*) PyString_AS_STRING(strpixels);
+ for (y=0; y<h; y++)
+ {
+ int opaque = 0;
+ for (x=0; x<w; x++)
+ {
+ unsigned int r = input[0];
+ unsigned int g = input[1];
+ unsigned int b = input[2];
+ input += 3;
+ if (((r<<16)|(g<<8)|b) == keycol)
+ opaque = 0;
+ else
+ {
+ if (!opaque)
+ {
+ *pblocks++ = x*bytes_per_pixel; /* start a new block */
+ opaque = 1;
+ }
+ pblocks[-1] += bytes_per_pixel<<16; /* add pixel to block */
+ pack_pixel(ppixels, r, g, b,
+ self->visual_info.depth, bytes_per_pixel);
+ ppixels += bytes_per_pixel;
+ }
+ }
+ *pblocks++ = 0; /* end-of-line marker block */
+ }
+
+ result = Py_BuildValue("iiOO", w, h, strblocks, strpixels);
+ Py_DECREF(strblocks);
+ Py_DECREF(strpixels);
+ return result;
+ }
+ else
+ {
+ XImage* image;
+ long extent;
+ unsigned char* data = NULL;
+ unsigned char* maskdata = NULL;
+ int scanline, bitmap_pad;
+ XPixmapObject* pm;
+
+ pm = new_pixmap(self, w, h, keycol>=0);
+ if (pm == NULL)
+ return NULL;
+
+ if (input == NULL)
+ return (PyObject*) pm; /* uninitialized pixmap */
+
+ extent = w*h;
+ if (3*extent != length)
+ {
+ PyErr_SetString(PyExc_ValueError, "bad string length");
+ goto err;
+ }
+
+ bitmap_pad = self->visual_info.depth >= 24 ? 32 : 16;
+ scanline = ((w+bitmap_pad-1) & ~(bitmap_pad-1)) / 8;
+ /*while (scanline&3) scanline++;*/
+ data = malloc(self->visual_info.depth*scanline*h);
+ if (data == NULL)
+ {
+ PyErr_NoMemory();
+ goto err;
+ }
+ memset(data, 0, self->visual_info.depth*scanline*h);
+ maskdata = malloc(self->visual_info.depth*scanline*h);
+ if (maskdata == NULL)
+ {
+ PyErr_NoMemory();
+ goto err;
+ }
+ memset(maskdata, 0, self->visual_info.depth*scanline*h);
+
+ {
+ int key_r = keycol>>16;
+ unsigned char key_g = keycol>>8;
+ unsigned char key_b = keycol>>0;
+ unsigned char* target = data;
+ unsigned char* masktarget = maskdata;
+ int plane, color;
+
+ unsigned int p_size[3];
+ switch( self->visual_info.depth )
+ {
+ case 15:
+ p_size[0] = p_size[1] = p_size[2] = 5;
+ break;
+ case 16:
+ p_size[0] = p_size[2] = 5;
+ p_size[1] = 6;
+ break;
+ case 24:
+ case 32:
+ p_size[0] = p_size[1] = p_size[2] = 8;
+ break;
+ }
+
+ for (color=0; color<3; color++)
+ for (plane=128; plane>=(1<<(8-p_size[color])); plane/=2)
+ {
+ unsigned char* src = input;
+ int x, y;
+ for (y=0; y<h; y++, target+=scanline, masktarget+=scanline)
+ for (x=0; x<w; x++, src+=3)
+ {
+ if (src[0] == key_r && src[1] == key_g && src[2] == key_b)
+ {
+ /* transparent */
+ masktarget[x/8] |= (1<<(x&7));
+ }
+ else
+ if (src[color] & plane)
+ target[x/8] |= (1<<(x&7));
+ }
+ }
+ }
+
+ if (keycol < 0)
+ free(maskdata);
+ else
+ {
+ image = XCreateImage(self->dpy, self->visual_info.visual,
+ self->visual_info.depth, XYPixmap, 0,
+ maskdata, w, h,
+ bitmap_pad, scanline);
+ if (image == NULL || image == (XImage*) -1)
+ {
+ PyErr_SetString(PyExc_IOError, "XCreateImage failed (2)");
+ goto err;
+ }
+ image->byte_order = LSBFirst;
+ image->bitmap_bit_order = LSBFirst;
+ maskdata = NULL;
+ XPutImage(self->dpy, pm->mask, self->gc, image, 0, 0, 0, 0, w, h);
+ XDestroyImage(image);
+ }
+
+ image = XCreateImage(self->dpy, self->visual_info.visual,
+ self->visual_info.depth, XYPixmap, 0,
+ data, w, h,
+ bitmap_pad, scanline);
+ if (image == NULL || image == (XImage*) -1)
+ {
+ PyErr_SetString(PyExc_IOError, "XCreateImage failed");
+ goto err;
+ }
+ image->byte_order = LSBFirst;
+ image->bitmap_bit_order = LSBFirst;
+ data = NULL;
+ XPutImage(self->dpy, pm->handle, self->gc, image, 0, 0, 0, 0, w, h);
+ XDestroyImage(image);
+
+ return (PyObject*) pm;
+
+ err:
+ free(maskdata);
+ free(data);
+ Py_DECREF(pm);
+ return NULL;
+ }
+}
+
+static PyObject* display_get(DisplayObject* self, int x, int y, int w, int h)
+{
+ if (self->shmmode)
+ {
+ int clipx=0, clipy=0, clipw=self->width, cliph=self->height;
+ int original_w, original_h;
+ int firstline=0, firstcol=0;
+ unsigned int bytes_per_pixel = self->plane.m_shm_image->bits_per_pixel/8;
+ unsigned char* data = get_dpy_data(self);
+ if (!data)
+ return NULL;
+
+ original_w = w;
+ original_h = h;
+ if (x<clipx) { firstcol=clipx-x; w+=x-clipx; x=clipx; }
+ if (y<clipy) { firstline=clipy-y; h+=y-clipy; y=clipy; }
+ if (x+w > clipw) w = clipw-x;
+ if (y+h > cliph) h = cliph-y;
+
+ {
+ int countblocks = original_h + ((w>0 && h>0) ? h : 0);
+ /* end blocks + real blocks */
+ int countpixels = (w>0 && h>0) ? w * h : 0;
+ PyObject* result;
+ PyObject* strblocks;
+ PyObject* strpixels;
+ unsigned int* pblocks;
+ unsigned char* ppixels;
+ int wbytes = w * bytes_per_pixel;
+ int block = (firstcol * bytes_per_pixel) | (wbytes << 16);
+ int data_scanline = bytes_per_pixel*self->width;
+
+ /* allocate memory */
+ strblocks = PyString_FromStringAndSize(NULL,
+ countblocks*sizeof(int));
+ if (strblocks == NULL)
+ return NULL;
+ strpixels = PyString_FromStringAndSize(NULL,
+ countpixels*bytes_per_pixel);
+ if (strpixels == NULL)
+ {
+ Py_DECREF(strblocks);
+ return NULL;
+ }
+
+ /* write data */
+ pblocks = (unsigned int*) PyString_AS_STRING(strblocks);
+ ppixels = (unsigned char*) PyString_AS_STRING(strpixels);
+ data += bytes_per_pixel*(x+y*self->width);
+ for (y=0; y<original_h; y++)
+ {
+ if (y >= firstline && y < firstline+h && w > 0)
+ {
+ *pblocks++ = block;
+ memcpy(ppixels, data, wbytes);
+ ppixels += wbytes;
+ data += data_scanline;
+ }
+ *pblocks++ = 0;
+ }
+
+ result = Py_BuildValue("iiOO", original_w, original_h,
+ strblocks, strpixels);
+ Py_DECREF(strblocks);
+ Py_DECREF(strpixels);
+ return result;
+ }
+ }
+ else
+ {
+ XPixmapObject* pm = new_pixmap(self, w, h, 0);
+ if (pm != NULL)
+ XCopyArea(self->dpy, self->backpixmap, pm->handle, self->gc,
+ x, y, w, h, 0, 0);
+ return (PyObject*) pm;
+ }
+}
+
+static PyObject* save_background(DisplayObject* self, int x, int y,
+ int w, int h, int save_bkgnd)
+{
+ if (save_bkgnd)
+ {
+ PyObject* pm = display_get(self, x, y, w, h);
+ PyObject* result;
+ if (pm == NULL)
+ return NULL;
+ result = Py_BuildValue("iiO", x, y, pm);
+ Py_DECREF(pm);
+ return result;
+ }
+ else
+ {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+}
+
+#define ALPHAFACTOR 2
+#define ALPHABLEND(maximum, x, y) ((maximum-y)*x/(maximum*ALPHAFACTOR) + y)
+
+static void memcpy_alpha_32(unsigned int* dst, unsigned int* src, int count)
+{
+ int i;
+ for (i=0; i<count/4; i++)
+ {
+ int x = dst[i];
+ int y = src[i];
+
+ int xr = x >> 16;
+ int xg = x & 0xff00;
+ int xb = x & 0xff;
+
+ int yr = y >> 16;
+ int yg = y & 0xff00;
+ int yb = y & 0xff;
+
+ int zr = ALPHABLEND(0xff, xr, yr);
+ int zg = ALPHABLEND(0xff00, xg, yg);
+ int zb = ALPHABLEND(0xff, xb, yb);
+
+ dst[i] = (zr << 16) | (zg & 0xff00) | zb;
+ }
+}
+
+static void memcpy_alpha_24(unsigned char* dst, unsigned char* src, int count)
+{
+ int i;
+ for (i=0; i<count; i++)
+ {
+ int x = dst[i];
+ int y = src[i];
+ dst[i] = ALPHABLEND(255, x, y);
+ }
+}
+
+static void memcpy_alpha_15(unsigned short* dst, unsigned short* src, int count)
+{
+ int i;
+ for (i=0; i<count/2; i++)
+ {
+ unsigned short x = dst[i];
+ unsigned short y = src[i];
+
+ int xr = x >> 10;
+ int xg = x & 0x03e0;
+ int xb = x & 0x001f;
+
+ int yr = y >> 10;
+ int yg = y & 0x03e0;
+ int yb = y & 0x001f;
+
+ int zr = ALPHABLEND(31, xr, yr);
+ int zg = ALPHABLEND(0x3e0, xg, yg);
+ int zb = ALPHABLEND(31, xb, yb);
+
+ dst[i] = (zr << 10) | (zg & 0x03e0) | zb;
+ }
+}
+
+static void memcpy_alpha_16(unsigned short* dst, unsigned short* src, int count)
+{
+ int i;
+ for (i=0; i<count/2; i++)
+ {
+ unsigned short x = dst[i];
+ unsigned short y = src[i];
+
+ int xr = x >> 11;
+ int xg = x & 0x07e0;
+ int xb = x & 0x001f;
+
+ int yr = y >> 11;
+ int yg = y & 0x07e0;
+ int yb = y & 0x001f;
+
+ int zr = ALPHABLEND(31, xr, yr);
+ int zg = ALPHABLEND(0x7e0, xg, yg);
+ int zb = ALPHABLEND(31, xb, yb);
+
+ dst[i] = (zr << 11) | (zg & 0x07e0) | zb;
+ }
+}
+
+typedef void (*memcpy_alpha_fn) (unsigned char*, unsigned char*, int);
+
+static PyObject* display_overlay(DisplayObject* self, PyObject* args,
+ int save_bkgnd)
+{
+ PyObject* result;
+
+ if (self->shmmode)
+ {
+ int x,y,w,h, original_x, original_y, original_w, original_h;
+ int data_scanline;
+ int clipx=0, clipy=0, clipw=65536, cliph=65536, alpha=255;
+ unsigned int* src;
+ unsigned char* srcdata;
+ unsigned char* original_srcdata;
+ int length1, length2, firstline=0, firstcol=0;
+ unsigned int bytes_per_pixel = self->plane.m_shm_image->bits_per_pixel/8;
+ memcpy_alpha_fn memcpy_alpha;
+ unsigned char* data = get_dpy_data(self);
+ if (!PyArg_ParseTuple(args, "ii(iis#s#)|(iiii)i",
+ &x, &y, &w, &h, &src, &length1, &srcdata, &length2,
+ &clipx, &clipy, &clipw, &cliph, &alpha) || !data)
+ return NULL;
+
+ original_x = x;
+ original_y = y;
+ original_w = w;
+ original_h = h;
+ original_srcdata = srcdata;
+ 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<clipx) { firstcol = clipx-x; w+=x-clipx; x=clipx; }
+ if (y<clipy) { firstline = clipy-y; h+=y-clipy; y=clipy; }
+ if (x+w > clipw) w = clipw-x;
+ if (y+h > cliph) h = cliph-y;
+ if (w > 0 && h > 0)
+ {
+ int dstoffset, blocksize;
+ unsigned int block;
+ data += bytes_per_pixel*(x+y*self->width);
+ data_scanline = bytes_per_pixel*self->width;
+
+ memcpy_alpha = (memcpy_alpha_fn) memcpy;
+ if (alpha < 255)
+ switch (self->visual_info.depth) {
+ case 15: memcpy_alpha = (memcpy_alpha_fn) memcpy_alpha_15; break;
+ case 16: memcpy_alpha = (memcpy_alpha_fn) memcpy_alpha_16; break;
+ case 24: memcpy_alpha = (memcpy_alpha_fn) memcpy_alpha_24; break;
+ case 32: memcpy_alpha = (memcpy_alpha_fn) memcpy_alpha_32; break;
+ }
+
+ /* 'structure' points to a sequence of int-sized blocks with the
+ following meaning:
+
+ n & 0xFFFF -- byte offset within the line
+ n >> 16 -- number of opaque bytes to copy there
+
+ n == 0 means end of line.
+ */
+
+ /* read and ignore 'firstline' complete lines */
+ while (firstline--)
+ {
+ while ((block = *src++) != 0)
+ {
+ blocksize = block >> 16;
+ srcdata += blocksize;
+ }
+ }
+
+ if (w == original_w)
+ {
+ if (!save_bkgnd)
+ {
+ /* common fast case: copy the whole width of the image */
+ do
+ {
+ while ((block = *src++) != 0)
+ {
+ dstoffset = block & 0xFFFF;
+ blocksize = block >> 16;
+ memcpy(data + dstoffset, srcdata, blocksize);
+ srcdata += blocksize;
+ }
+ data += data_scanline;
+ }
+ while (--h);
+ result = Py_None;
+ Py_INCREF(result);
+ }
+ else
+ {
+ /* copy and save the background */
+ PyObject* cliprect;
+ PyObject* strblocks;
+ PyObject* strpixels;
+ unsigned char* ppixels;
+
+ strpixels = PyString_FromStringAndSize(NULL, length2);
+ if (strpixels == NULL)
+ return NULL;
+ ppixels = (unsigned char*) PyString_AS_STRING(strpixels);
+ ppixels += srcdata - original_srcdata;
+
+ do
+ {
+ while ((block = *src++) != 0)
+ {
+ dstoffset = block & 0xFFFF;
+ blocksize = block >> 16;
+ memcpy(ppixels, data + dstoffset, blocksize);
+ ppixels += blocksize;
+ memcpy_alpha(data + dstoffset, srcdata, blocksize);
+ srcdata += blocksize;
+ }
+ data += data_scanline;
+ }
+ while (--h);
+
+ strblocks = PyTuple_GET_ITEM(PyTuple_GET_ITEM(args, 2), 2);
+ if (PyTuple_GET_SIZE(args) > 3)
+ {
+ cliprect = PyTuple_GET_ITEM(args, 3);
+ result = Py_BuildValue("ii(iiOO)O",
+ original_x,
+ original_y,
+ original_w,
+ original_h,
+ strblocks,
+ strpixels,
+ cliprect);
+ }
+ else
+ {
+ result = Py_BuildValue("ii(iiOO)",
+ original_x,
+ original_y,
+ original_w,
+ original_h,
+ strblocks,
+ strpixels);
+ }
+ Py_DECREF(strpixels);
+ }
+ }
+ else
+ {
+ /* byte offsets within a line */
+ unsigned char* blocksrc;
+ int skip, lastcol;
+
+ result = save_background(self, x, y, w, h, save_bkgnd);
+
+ lastcol = (firstcol + w) * bytes_per_pixel;
+ firstcol *= bytes_per_pixel;
+
+ /* slow case: only copy a portion of the width of the image */
+ data -= firstcol;
+ do
+ {
+ while ((block = *src++) != 0)
+ {
+ dstoffset = block & 0xFFFF;
+ blocksize = block >> 16;
+ blocksrc = srcdata;
+ srcdata += blocksize;
+ skip = firstcol - dstoffset;
+ if (skip < 0)
+ skip = 0;
+ if (blocksize > lastcol - dstoffset)
+ blocksize = lastcol - dstoffset;
+ if (blocksize > skip)
+ memcpy_alpha(data + dstoffset + skip, blocksrc + skip,
+ blocksize - skip);
+ }
+ data += data_scanline;
+ }
+ while (--h);
+ }
+ }
+ else
+ {
+ result = args;
+ Py_INCREF(result);
+ }
+ }
+ else
+ {
+ int x,y, x1=0,y1=0,w1=-1,h1=-1,alpha;
+ XPixmapObject* pm;
+
+ if (!checkopen(self))
+ return NULL;
+ if (!PyArg_ParseTuple(args, "iiO!|(iiii)i", &x, &y, &XPixmap_Type, &pm,
+ &x1, &y1, &w1, &h1, &alpha))
+ return NULL;
+
+ if (w1 < 0)
+ w1 = pm->width;
+ if (h1 < 0)
+ h1 = pm->height;
+
+ result = save_background(self, x, y, w1, h1, save_bkgnd);
+
+ if (pm->mask == (Pixmap) -1)
+ {
+ XCopyArea(self->dpy, pm->handle, self->backpixmap, self->gc,
+ x1, y1, w1, h1, x, y);
+ }
+ else
+ {
+ XCopyArea(self->dpy, pm->mask, self->backpixmap, self->gc_and,
+ x1, y1, w1, h1, x, y);
+ XCopyArea(self->dpy, pm->handle, self->backpixmap, self->gc_or,
+ x1, y1, w1, h1, x, y);
+ }
+ }
+ return result;
+}
+
+static PyObject* display_putppm1(DisplayObject* self, PyObject* args)
+{
+ return display_overlay(self, args, 0);
+}
+
+static PyObject* display_overlayppm1(DisplayObject* self, PyObject* args)
+{
+ return display_overlay(self, args, 1);
+}
+
+static PyObject* display_getppm1(DisplayObject* self, PyObject* args)
+{
+ int x, y, w, h;
+ if (!checkopen(self))
+ return NULL;
+ if (!PyArg_ParseTuple(args, "(iiii)", &x, &y, &w, &h))
+ return NULL;
+ return display_get(self, x, y, w, h);
+}
+
+static int readXevents(DisplayObject* self)
+{
+ while (XEventsQueued(self->dpy, QueuedAfterReading) > 0)
+ {
+ XEvent e;
+ XNextEvent(self->dpy, &e);
+ switch (e.type) {
+ case KeyPress:
+ case KeyRelease:
+ {
+ KeySym sym;
+ PyObject* v;
+ int err;
+ if (self->keyevents == NULL)
+ {
+ self->keyevents = PyList_New(0);
+ if (self->keyevents == NULL)
+ return 0;
+ }
+ sym = XLookupKeysym(&e.xkey,0);
+ v = Py_BuildValue("ii", sym, e.type);
+ if (v == NULL)
+ return 0;
+ err = PyList_Append(self->keyevents, v);
+ Py_DECREF(v);
+ if (err)
+ return 0;
+ break;
+ }
+ case ButtonPress:
+ {
+ PyObject* v;
+ int err;
+ if (self->mouseevents == NULL)
+ {
+ self->mouseevents = PyList_New(0);
+ if (self->mouseevents == NULL)
+ return 0;
+ }
+ v = Py_BuildValue("ii", e.xbutton.x, e.xbutton.y);
+ if (v == NULL)
+ return 0;
+ err = PyList_Append(self->mouseevents, v);
+ Py_DECREF(v);
+ if (err)
+ return 0;
+ break;
+ }
+ case MotionNotify:
+ {
+ Py_XDECREF(self->motionevent);
+ self->motionevent = Py_BuildValue("ii", e.xmotion.x, e.xmotion.y);
+ if (self->motionevent == NULL)
+ return 0;
+ break;
+ }
+ }
+ }
+ return 1;
+}
+
+#define ENABLE_EVENTS(mask) do { \
+ if (!(self->selectinput & (mask))) \
+ { \
+ self->selectinput |= (mask); \
+ XSelectInput(self->dpy, self->win, self->selectinput); \
+ } \
+} while (0)
+
+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)
+{
+ if (!checkopen(self))
+ return NULL;
+
+ if (self->shmmode)
+ {
+ XShmPutImage(self->dpy, self->win, self->gc,
+ self->plane.m_shm_image,
+ 0, 0, 0, 0,
+ self->plane.m_width,
+ self->plane.m_height,
+ False);
+ }
+ else
+ {
+ XCopyArea(self->dpy, self->backpixmap, self->win, self->gc,
+ 0, 0, self->width, self->height, 0, 0);
+ }
+ flush(self);
+ if (!readXevents(self))
+ return NULL;
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject* display_fd1(DisplayObject* self, PyObject *args)
+{
+ return PyInt_FromLong(ConnectionNumber(self->dpy));
+}
+
+static PyObject* display_shmmode(DisplayObject* self, PyObject *args)
+{
+ return PyInt_FromLong(self->shmmode);
+}
+
+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},
+ {"overlayppm",(PyCFunction)display_overlayppm1, METH_VARARGS, NULL},
+ {"keyevents",(PyCFunction)display_keyevents1,METH_VARARGS, NULL},
+ {"mouseevents",(PyCFunction)display_mouseevents1,METH_VARARGS,NULL},
+ {"pointermotion",(PyCFunction)display_pointermotion1,METH_VARARGS,NULL},
+ {"fd", (PyCFunction)display_fd1, METH_VARARGS, NULL},
+ {"shmmode", (PyCFunction)display_shmmode, 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*/
+};
+
+statichere PyTypeObject XPixmap_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "Pixmap", /*tp_name*/
+ sizeof(XPixmapObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ (destructor)pixmap_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*/
+};
+
+
+static PyMethodDef ShmMethods[] = {
+ {"Display", new_display, METH_VARARGS},
+ {NULL, NULL} /* Sentinel */
+ };
+
+void initxshm(void)
+{
+ Display_Type.ob_type = &PyType_Type;
+ XPixmap_Type.ob_type = &PyType_Type;
+ Py_InitModule("xshm", ShmMethods);
+}
diff --git a/display/xshm.so b/display/xshm.so
new file mode 100755
index 0000000..2296a67
--- /dev/null
+++ b/display/xshm.so
Binary files differ