468 lines
14 KiB
Python
468 lines
14 KiB
Python
|
# pygame - Python Game Library
|
||
|
# Copyright (C) 2000-2003 Pete Shinners
|
||
|
#
|
||
|
# This library is free software; you can redistribute it and/or
|
||
|
# modify it under the terms of the GNU Library General Public
|
||
|
# License as published by the Free Software Foundation; either
|
||
|
# version 2 of the License, or (at your option) any later version.
|
||
|
#
|
||
|
# This library is distributed in the hope that it will be useful,
|
||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
# Library General Public License for more details.
|
||
|
#
|
||
|
# You should have received a copy of the GNU Library General Public
|
||
|
# License along with this library; if not, write to the Free
|
||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||
|
#
|
||
|
# Pete Shinners
|
||
|
# pete@shinners.org
|
||
|
|
||
|
"""Set of cursor resources available for use. These cursors come
|
||
|
in a sequence of values that are needed as the arguments for
|
||
|
pygame.mouse.set_cursor(). To dereference the sequence in place
|
||
|
and create the cursor in one step, call like this:
|
||
|
pygame.mouse.set_cursor(*pygame.cursors.arrow).
|
||
|
|
||
|
Here is a list of available cursors:
|
||
|
arrow, diamond, ball, broken_x, tri_left, tri_right
|
||
|
|
||
|
There is also a sample string cursor named 'thickarrow_strings'.
|
||
|
The compile() function can convert these string cursors into cursor byte data.
|
||
|
"""
|
||
|
|
||
|
import pygame
|
||
|
_cursor_id_table = {
|
||
|
pygame.SYSTEM_CURSOR_ARROW: "SYSTEM_CURSOR_ARROW",
|
||
|
pygame.SYSTEM_CURSOR_IBEAM: "SYSTEM_CURSOR_IBEAM",
|
||
|
pygame.SYSTEM_CURSOR_WAIT: "SYSTEM_CURSOR_WAIT",
|
||
|
pygame.SYSTEM_CURSOR_CROSSHAIR: "SYSTEM_CURSOR_CROSSHAIR",
|
||
|
pygame.SYSTEM_CURSOR_WAITARROW: "SYSTEM_CURSOR_WAITARROW",
|
||
|
pygame.SYSTEM_CURSOR_SIZENWSE: "SYSTEM_CURSOR_SIZENWSE",
|
||
|
pygame.SYSTEM_CURSOR_SIZENESW: "SYSTEM_CURSOR_SIZENESW",
|
||
|
pygame.SYSTEM_CURSOR_SIZEWE: "SYSTEM_CURSOR_SIZEWE",
|
||
|
pygame.SYSTEM_CURSOR_SIZENS: "SYSTEM_CURSOR_SIZENS",
|
||
|
pygame.SYSTEM_CURSOR_SIZEALL: "SYSTEM_CURSOR_SIZEALL",
|
||
|
pygame.SYSTEM_CURSOR_NO: "SYSTEM_CURSOR_NO",
|
||
|
pygame.SYSTEM_CURSOR_HAND: "SYSTEM_CURSOR_HAND",
|
||
|
}
|
||
|
|
||
|
class Cursor(object):
|
||
|
def __init__(self, *args):
|
||
|
if len(args) == 0:
|
||
|
self.type = "system"
|
||
|
self.data = (pygame.SYSTEM_CURSOR_ARROW,)
|
||
|
elif len(args) == 1 and args[0] in list(_cursor_id_table.keys()):
|
||
|
self.type = "system"
|
||
|
self.data = (args[0],)
|
||
|
elif len(args) == 1 and isinstance(args[0], Cursor):
|
||
|
self.type = args[0].type
|
||
|
self.data = args[0].data
|
||
|
elif len(args) == 2 and len(args[0]) == 2 and isinstance(args[1], pygame.Surface):
|
||
|
self.type = "color"
|
||
|
self.data = tuple(args)
|
||
|
elif len(args) == 4 and len(args[0]) == 2 and len(args[1]) == 2:
|
||
|
self.type = "bitmap"
|
||
|
self.data = tuple([tuple(arg) for arg in args])
|
||
|
else:
|
||
|
raise TypeError("Arguments must match a cursor specification")
|
||
|
|
||
|
def __len__(self):
|
||
|
return len(self.data)
|
||
|
|
||
|
def __getitem__(self, index):
|
||
|
return self.data[index]
|
||
|
|
||
|
def __eq__(self, other):
|
||
|
return isinstance(other, Cursor) and self.data == other.data
|
||
|
|
||
|
def __ne__(self, other):
|
||
|
return not self.__eq__(other)
|
||
|
|
||
|
def __hash__(self):
|
||
|
return hash(tuple([self.type] + [data for data in self.data]))
|
||
|
|
||
|
def __repr__(self):
|
||
|
if self.type == "system":
|
||
|
id_string = _cursor_id_table.get(self.data[0], "constant lookup error")
|
||
|
return "<Cursor(type: system, constant: " + id_string +")>"
|
||
|
if self.type == "bitmap":
|
||
|
size = "size: " + str(self.data[0])
|
||
|
hotspot = "hotspot: " + str(self.data[1])
|
||
|
return "<Cursor(type: bitmap, " + size + ", " + hotspot +")>"
|
||
|
if self.type == "color":
|
||
|
hotspot = "hotspot: " + str(self.data[0])
|
||
|
surf = repr(self.data[1])
|
||
|
return "<Cursor(type: color, " + hotspot + ", surf: " + surf +")>"
|
||
|
raise TypeError("Invalid Cursor")
|
||
|
|
||
|
|
||
|
# Python side of the set_cursor function: C side in mouse.c
|
||
|
def set_cursor(*args):
|
||
|
"""set_cursor(pygame.cursors.Cursor OR args for a pygame.cursors.Cursor) -> None
|
||
|
set the mouse cursor to a new cursor"""
|
||
|
cursor = Cursor(*args)
|
||
|
pygame.mouse._set_cursor(**{cursor.type:cursor.data})
|
||
|
pygame.mouse.set_cursor = set_cursor
|
||
|
del set_cursor # cleanup namespace
|
||
|
|
||
|
# Python side of the get_cursor function: C side in mouse.c
|
||
|
def get_cursor():
|
||
|
"""get_cursor() -> pygame.cursors.Cursor
|
||
|
get the current mouse cursor"""
|
||
|
return Cursor(*pygame.mouse._get_cursor())
|
||
|
pygame.mouse.get_cursor = get_cursor
|
||
|
del get_cursor # cleanup namespace
|
||
|
|
||
|
arrow = Cursor(
|
||
|
(16, 16),
|
||
|
(0, 0),
|
||
|
(
|
||
|
0x00, 0x00, 0x40, 0x00, 0x60, 0x00, 0x70, 0x00,
|
||
|
0x78, 0x00, 0x7C, 0x00, 0x7E, 0x00, 0x7F, 0x00,
|
||
|
0x7F, 0x80, 0x7C, 0x00, 0x6C, 0x00, 0x46, 0x00,
|
||
|
0x06, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||
|
),
|
||
|
(
|
||
|
0x40, 0x00, 0xE0, 0x00, 0xF0, 0x00, 0xF8, 0x00,
|
||
|
0xFC, 0x00, 0xFE, 0x00, 0xFF, 0x00, 0xFF, 0x80,
|
||
|
0xFF, 0xC0, 0xFF, 0x80, 0xFE, 0x00, 0xEF, 0x00,
|
||
|
0x4F, 0x00, 0x07, 0x80, 0x07, 0x80, 0x03, 0x00,
|
||
|
),
|
||
|
)
|
||
|
|
||
|
diamond = Cursor(
|
||
|
(16, 16),
|
||
|
(7, 7),
|
||
|
(
|
||
|
0, 0, 1, 0, 3, 128, 7, 192,
|
||
|
14, 224, 28, 112, 56, 56, 112, 28,
|
||
|
56, 56, 28, 112, 14, 224, 7, 192,
|
||
|
3, 128, 1, 0, 0, 0, 0, 0,
|
||
|
),
|
||
|
(
|
||
|
1, 0, 3, 128, 7, 192, 15, 224,
|
||
|
31, 240, 62, 248, 124, 124, 248, 62,
|
||
|
124, 124, 62, 248, 31, 240, 15, 224,
|
||
|
7, 192, 3, 128, 1, 0, 0, 0,
|
||
|
),
|
||
|
)
|
||
|
|
||
|
ball = Cursor(
|
||
|
(16, 16),
|
||
|
(7, 7),
|
||
|
(
|
||
|
0, 0, 3, 192, 15, 240, 24, 248,
|
||
|
51, 252, 55, 252, 127, 254, 127, 254,
|
||
|
127, 254, 127, 254, 63, 252, 63, 252,
|
||
|
31, 248, 15, 240, 3, 192, 0, 0,
|
||
|
),
|
||
|
(
|
||
|
3, 192, 15, 240, 31, 248, 63, 252,
|
||
|
127, 254, 127, 254, 255, 255, 255, 255,
|
||
|
255, 255, 255, 255, 127, 254, 127, 254,
|
||
|
63, 252, 31, 248, 15, 240, 3, 192,
|
||
|
),
|
||
|
)
|
||
|
|
||
|
broken_x = Cursor(
|
||
|
(16, 16),
|
||
|
(7, 7),
|
||
|
(
|
||
|
0, 0, 96, 6, 112, 14, 56, 28,
|
||
|
28, 56, 12, 48, 0, 0, 0, 0,
|
||
|
0, 0, 0, 0, 12, 48, 28, 56,
|
||
|
56, 28, 112, 14, 96, 6, 0, 0,
|
||
|
),
|
||
|
(
|
||
|
224, 7, 240, 15, 248, 31, 124, 62,
|
||
|
62, 124, 30, 120, 14, 112, 0, 0,
|
||
|
0, 0, 14, 112, 30, 120, 62, 124,
|
||
|
124, 62, 248, 31, 240, 15, 224, 7,
|
||
|
),
|
||
|
)
|
||
|
|
||
|
tri_left = Cursor(
|
||
|
(16, 16),
|
||
|
(1, 1),
|
||
|
(
|
||
|
0, 0, 96, 0, 120, 0, 62, 0,
|
||
|
63, 128, 31, 224, 31, 248, 15, 254,
|
||
|
15, 254, 7, 128, 7, 128, 3, 128,
|
||
|
3, 128, 1, 128, 1, 128, 0, 0,
|
||
|
),
|
||
|
(
|
||
|
224, 0, 248, 0, 254, 0, 127, 128,
|
||
|
127, 224, 63, 248, 63, 254, 31, 255,
|
||
|
31, 255, 15, 254, 15, 192, 7, 192,
|
||
|
7, 192, 3, 192, 3, 192, 1, 128,
|
||
|
),
|
||
|
)
|
||
|
|
||
|
tri_right = Cursor(
|
||
|
(16, 16),
|
||
|
(14, 1),
|
||
|
(
|
||
|
0, 0, 0, 6, 0, 30, 0, 124,
|
||
|
1, 252, 7, 248, 31, 248, 127, 240,
|
||
|
127, 240, 1, 224, 1, 224, 1, 192,
|
||
|
1, 192, 1, 128, 1, 128, 0, 0,
|
||
|
),
|
||
|
(
|
||
|
0, 7, 0, 31, 0, 127, 1, 254,
|
||
|
7, 254, 31, 252, 127, 252, 255, 248,
|
||
|
255, 248, 127, 240, 3, 240, 3, 224,
|
||
|
3, 224, 3, 192, 3, 192, 1, 128,
|
||
|
),
|
||
|
)
|
||
|
|
||
|
|
||
|
# Here is an example string resource cursor. To use this:
|
||
|
# curs, mask = pygame.cursors.compile_cursor(pygame.cursors.thickarrow_strings, 'X', '.')
|
||
|
# pygame.mouse.set_cursor((24, 24), (0, 0), curs, mask)
|
||
|
|
||
|
# sized 24x24
|
||
|
thickarrow_strings = (
|
||
|
"XX ",
|
||
|
"XXX ",
|
||
|
"XXXX ",
|
||
|
"XX.XX ",
|
||
|
"XX..XX ",
|
||
|
"XX...XX ",
|
||
|
"XX....XX ",
|
||
|
"XX.....XX ",
|
||
|
"XX......XX ",
|
||
|
"XX.......XX ",
|
||
|
"XX........XX ",
|
||
|
"XX........XXX ",
|
||
|
"XX......XXXXX ",
|
||
|
"XX.XXX..XX ",
|
||
|
"XXXX XX..XX ",
|
||
|
"XX XX..XX ",
|
||
|
" XX..XX ",
|
||
|
" XX..XX ",
|
||
|
" XX..XX ",
|
||
|
" XXXX ",
|
||
|
" XX ",
|
||
|
" ",
|
||
|
" ",
|
||
|
" ",
|
||
|
)
|
||
|
|
||
|
# sized 24x16
|
||
|
sizer_x_strings = (
|
||
|
" X X ",
|
||
|
" XX XX ",
|
||
|
" X.X X.X ",
|
||
|
" X..X X..X ",
|
||
|
" X...XXXXXXXX...X ",
|
||
|
"X................X ",
|
||
|
" X...XXXXXXXX...X ",
|
||
|
" X..X X..X ",
|
||
|
" X.X X.X ",
|
||
|
" XX XX ",
|
||
|
" X X ",
|
||
|
" ",
|
||
|
" ",
|
||
|
" ",
|
||
|
" ",
|
||
|
" ",
|
||
|
)
|
||
|
|
||
|
# sized 16x24
|
||
|
sizer_y_strings = (
|
||
|
" X ",
|
||
|
" X.X ",
|
||
|
" X...X ",
|
||
|
" X.....X ",
|
||
|
" X.......X ",
|
||
|
"XXXXX.XXXXX ",
|
||
|
" X.X ",
|
||
|
" X.X ",
|
||
|
" X.X ",
|
||
|
" X.X ",
|
||
|
" X.X ",
|
||
|
" X.X ",
|
||
|
" X.X ",
|
||
|
"XXXXX.XXXXX ",
|
||
|
" X.......X ",
|
||
|
" X.....X ",
|
||
|
" X...X ",
|
||
|
" X.X ",
|
||
|
" X ",
|
||
|
" ",
|
||
|
" ",
|
||
|
" ",
|
||
|
" ",
|
||
|
" ",
|
||
|
)
|
||
|
|
||
|
# sized 24x16
|
||
|
sizer_xy_strings = (
|
||
|
"XXXXXXXX ",
|
||
|
"X.....X ",
|
||
|
"X....X ",
|
||
|
"X...X ",
|
||
|
"X..X.X ",
|
||
|
"X.X X.X ",
|
||
|
"XX X.X X ",
|
||
|
"X X.X XX ",
|
||
|
" X.XX.X ",
|
||
|
" X...X ",
|
||
|
" X...X ",
|
||
|
" X....X ",
|
||
|
" X.....X ",
|
||
|
" XXXXXXXX ",
|
||
|
" ",
|
||
|
" ",
|
||
|
)
|
||
|
|
||
|
# sized 8x16
|
||
|
textmarker_strings = (
|
||
|
"ooo ooo ",
|
||
|
" o ",
|
||
|
" o ",
|
||
|
" o ",
|
||
|
" o ",
|
||
|
" o ",
|
||
|
" o ",
|
||
|
" o ",
|
||
|
" o ",
|
||
|
" o ",
|
||
|
" o ",
|
||
|
"ooo ooo ",
|
||
|
" ",
|
||
|
" ",
|
||
|
" ",
|
||
|
" ",
|
||
|
)
|
||
|
|
||
|
|
||
|
def compile(strings, black='X', white='.', xor='o'):
|
||
|
"""pygame.cursors.compile(strings, black, white, xor) -> data, mask
|
||
|
compile cursor strings into cursor data
|
||
|
|
||
|
This takes a set of strings with equal length and computes
|
||
|
the binary data for that cursor. The string widths must be
|
||
|
divisible by 8.
|
||
|
|
||
|
The black and white arguments are single letter strings that
|
||
|
tells which characters will represent black pixels, and which
|
||
|
characters represent white pixels. All other characters are
|
||
|
considered clear.
|
||
|
|
||
|
Some systems allow you to set a special toggle color for the
|
||
|
system color, this is also called the xor color. If the system
|
||
|
does not support xor cursors, that color will simply be black.
|
||
|
|
||
|
This returns a tuple containing the cursor data and cursor mask
|
||
|
data. Both these arguments are used when setting a cursor with
|
||
|
pygame.mouse.set_cursor().
|
||
|
"""
|
||
|
# first check for consistent lengths
|
||
|
size = len(strings[0]), len(strings)
|
||
|
if size[0] % 8 or size[1] % 8:
|
||
|
raise ValueError("cursor string sizes must be divisible by 8 %s" %
|
||
|
(size,))
|
||
|
|
||
|
for s in strings[1:]:
|
||
|
if len(s) != size[0]:
|
||
|
raise ValueError("Cursor strings are inconsistent lengths")
|
||
|
|
||
|
# create the data arrays.
|
||
|
# this could stand a little optimizing
|
||
|
maskdata = []
|
||
|
filldata = []
|
||
|
maskitem = fillitem = 0
|
||
|
step = 8
|
||
|
for s in strings:
|
||
|
for c in s:
|
||
|
maskitem = maskitem << 1
|
||
|
fillitem = fillitem << 1
|
||
|
step = step - 1
|
||
|
if c == black:
|
||
|
maskitem = maskitem | 1
|
||
|
fillitem = fillitem | 1
|
||
|
elif c == white:
|
||
|
maskitem = maskitem | 1
|
||
|
elif c == xor:
|
||
|
fillitem = fillitem | 1
|
||
|
|
||
|
if not step:
|
||
|
maskdata.append(maskitem)
|
||
|
filldata.append(fillitem)
|
||
|
maskitem = fillitem = 0
|
||
|
step = 8
|
||
|
|
||
|
return tuple(filldata), tuple(maskdata)
|
||
|
|
||
|
|
||
|
def load_xbm(curs, mask):
|
||
|
"""pygame.cursors.load_xbm(cursorfile, maskfile) -> cursor_args
|
||
|
reads a pair of XBM files into set_cursor arguments
|
||
|
|
||
|
Arguments can either be filenames or filelike objects
|
||
|
with the readlines method. Not largely tested, but
|
||
|
should work with typical XBM files.
|
||
|
"""
|
||
|
def bitswap(num):
|
||
|
val = 0
|
||
|
for x in range(8):
|
||
|
b = num&(1<<x) != 0
|
||
|
val = val<<1 | b
|
||
|
return val
|
||
|
|
||
|
if type(curs) is type(''):
|
||
|
with open(curs) as cursor_f:
|
||
|
curs = cursor_f.readlines()
|
||
|
else:
|
||
|
curs = curs.readlines()
|
||
|
|
||
|
if type(mask) is type(''):
|
||
|
with open(mask) as mask_f:
|
||
|
mask = mask_f.readlines()
|
||
|
else:
|
||
|
mask = mask.readlines()
|
||
|
|
||
|
# avoid comments
|
||
|
for i, line in enumerate(curs):
|
||
|
if line.startswith("#define"):
|
||
|
curs = curs[i:]
|
||
|
break
|
||
|
|
||
|
for i, line in enumerate(mask):
|
||
|
if line.startswith("#define"):
|
||
|
mask = mask[i:]
|
||
|
break
|
||
|
|
||
|
# load width,height
|
||
|
width = int(curs[0].split()[-1])
|
||
|
height = int(curs[1].split()[-1])
|
||
|
# load hotspot position
|
||
|
if curs[2].startswith('#define'):
|
||
|
hotx = int(curs[2].split()[-1])
|
||
|
hoty = int(curs[3].split()[-1])
|
||
|
else:
|
||
|
hotx = hoty = 0
|
||
|
|
||
|
info = width, height, hotx, hoty
|
||
|
|
||
|
possible_starts = ('static char', 'static unsigned char')
|
||
|
for i, line in enumerate(curs):
|
||
|
if line.startswith(possible_starts):
|
||
|
break
|
||
|
data = ' '.join(curs[i+1:]).replace('};', '').replace(',', ' ')
|
||
|
cursdata = []
|
||
|
for x in data.split():
|
||
|
cursdata.append(bitswap(int(x, 16)))
|
||
|
cursdata = tuple(cursdata)
|
||
|
for i, line in enumerate(mask):
|
||
|
if line.startswith(possible_starts):
|
||
|
break
|
||
|
data = ' '.join(mask[i+1:]).replace('};', '').replace(',', ' ')
|
||
|
maskdata = []
|
||
|
for x in data.split():
|
||
|
maskdata.append(bitswap(int(x, 16)))
|
||
|
|
||
|
maskdata = tuple(maskdata)
|
||
|
return info[:2], info[2:], cursdata, maskdata
|