You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

836 lines
34 KiB

2 years ago
# Xlib.xobject.drawable -- drawable objects (window and pixmap)
#
# Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1
# 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc.,
# 59 Temple Place,
# Suite 330,
# Boston, MA 02111-1307 USA
from Xlib import X, Xatom, Xutil
from Xlib.protocol import request, rq
# Other X resource objects
from . import resource
from . import colormap
from . import cursor
from . import fontable
# Inter-client communication conventions
from . import icccm
class Drawable(resource.Resource):
__drawable__ = resource.Resource.__resource__
def get_geometry(self):
return request.GetGeometry(display = self.display,
drawable = self)
def create_pixmap(self, width, height, depth):
pid = self.display.allocate_resource_id()
request.CreatePixmap(display = self.display,
depth = depth,
pid = pid,
drawable = self.id,
width = width,
height = height)
cls = self.display.get_resource_class('pixmap', Pixmap)
return cls(self.display, pid, owner = 1)
def create_gc(self, **keys):
cid = self.display.allocate_resource_id()
request.CreateGC(display = self.display,
cid = cid,
drawable = self.id,
attrs = keys)
cls = self.display.get_resource_class('gc', fontable.GC)
return cls(self.display, cid, owner = 1)
def copy_area(self, gc, src_drawable, src_x, src_y, width, height, dst_x, dst_y, onerror = None):
request.CopyArea(display = self.display,
onerror = onerror,
src_drawable = src_drawable,
dst_drawable = self.id,
gc = gc,
src_x = src_x,
src_y = src_y,
dst_x = dst_x,
dst_y = dst_y,
width = width,
height = height)
def copy_plane(self, gc, src_drawable, src_x, src_y, width, height,
dst_x, dst_y, bit_plane, onerror = None):
request.CopyPlane(display = self.display,
onerror = onerror,
src_drawable = src_drawable,
dst_drawable = self.id,
gc = gc,
src_x = src_x,
src_y = src_y,
dst_x = dst_x,
dst_y = dst_y,
width = width,
height = height,
bit_plane = bit_plane)
def poly_point(self, gc, coord_mode, points, onerror = None):
request.PolyPoint(display = self.display,
onerror = onerror,
coord_mode = coord_mode,
drawable = self.id,
gc = gc,
points = points)
def point(self, gc, x, y, onerror = None):
request.PolyPoint(display = self.display,
onerror = onerror,
coord_mode = X.CoordModeOrigin,
drawable = self.id,
gc = gc,
points = [(x, y)])
def poly_line(self, gc, coord_mode, points, onerror = None):
request.PolyLine(display = self.display,
onerror = onerror,
coord_mode = coord_mode,
drawable = self.id,
gc = gc,
points = points)
def line(self, gc, x1, y1, x2, y2, onerror = None):
request.PolySegment(display = self.display,
onerror = onerror,
drawable = self.id,
gc = gc,
segments = [(x1, y1, x2, y2)])
def poly_segment(self, gc, segments, onerror = None):
request.PolySegment(display = self.display,
onerror = onerror,
drawable = self.id,
gc = gc,
segments = segments)
def poly_rectangle(self, gc, rectangles, onerror = None):
request.PolyRectangle(display = self.display,
onerror = onerror,
drawable = self.id,
gc = gc,
rectangles = rectangles)
def rectangle(self, gc, x, y, width, height, onerror = None):
request.PolyRectangle(display = self.display,
onerror = onerror,
drawable = self.id,
gc = gc,
rectangles = [(x, y, width, height)])
def poly_arc(self, gc, arcs, onerror = None):
request.PolyArc(display = self.display,
onerror = onerror,
drawable = self.id,
gc = gc,
arcs = arcs)
def arc(self, gc, x, y, width, height, angle1, angle2, onerror = None):
request.PolyArc(display = self.display,
onerror = onerror,
drawable = self.id,
gc = gc,
arcs = [(x, y, width, height, angle1, angle2)])
def fill_poly(self, gc, shape, coord_mode, points, onerror = None):
request.FillPoly(display = self.display,
onerror = onerror,
shape = shape,
coord_mode = coord_mode,
drawable = self.id,
gc = gc,
points = points)
def poly_fill_rectangle(self, gc, rectangles, onerror = None):
request.PolyFillRectangle(display = self.display,
onerror = onerror,
drawable = self.id,
gc = gc,
rectangles = rectangles)
def fill_rectangle(self, gc, x, y, width, height, onerror = None):
request.PolyFillRectangle(display = self.display,
onerror = onerror,
drawable = self.id,
gc = gc,
rectangles = [(x, y, width, height)])
def poly_fill_arc(self, gc, arcs, onerror = None):
request.PolyFillArc(display = self.display,
onerror = onerror,
drawable = self.id,
gc = gc,
arcs = arcs)
def fill_arc(self, gc, x, y, width, height, angle1, angle2, onerror = None):
request.PolyFillArc(display = self.display,
onerror = onerror,
drawable = self.id,
gc = gc,
arcs = [(x, y, width, height, angle1, angle2)])
def put_image(self, gc, x, y, width, height, format,
depth, left_pad, data, onerror = None):
request.PutImage(display = self.display,
onerror = onerror,
format = format,
drawable = self.id,
gc = gc,
width = width,
height = height,
dst_x = x,
dst_y = y,
left_pad = left_pad,
depth = depth,
data = data)
# Trivial little method for putting PIL images. Will break on anything
# but depth 1 or 24...
def put_pil_image(self, gc, x, y, image, onerror = None):
width, height = image.size
if image.mode == '1':
format = X.XYBitmap
depth = 1
if self.display.info.bitmap_format_bit_order == 0:
rawmode = '1;R'
else:
rawmode = '1'
pad = self.display.info.bitmap_format_scanline_pad
stride = roundup(width, pad) >> 3
elif image.mode == 'RGB':
format = X.ZPixmap
depth = 24
if self.display.info.image_byte_order == 0:
rawmode = 'BGRX'
else:
rawmode = 'RGBX'
pad = self.display.info.bitmap_format_scanline_pad
unit = self.display.info.bitmap_format_scanline_unit
stride = roundup(width * unit, pad) >> 3
else:
raise ValueError('Unknown data format')
maxlen = (self.display.info.max_request_length << 2) \
- request.PutImage._request.static_size
split = maxlen // stride
x1 = 0
x2 = width
y1 = 0
while y1 < height:
h = min(height, split)
if h < height:
subimage = image.crop((x1, y1, x2, y1 + h))
else:
subimage = image
w, h = subimage.size
data = subimage.tobytes("raw", rawmode, stride, 0)
self.put_image(gc, x, y, w, h, format, depth, 0, data)
y1 = y1 + h
y = y + h
def get_image(self, x, y, width, height, format, plane_mask):
return request.GetImage(display = self.display,
format = format,
drawable = self.id,
x = x,
y = y,
width = width,
height = height,
plane_mask = plane_mask)
def draw_text(self, gc, x, y, text, onerror = None):
request.PolyText8(display = self.display,
onerror = onerror,
drawable = self.id,
gc = gc,
x = x,
y = y,
items = [text])
def poly_text(self, gc, x, y, items, onerror = None):
request.PolyText8(display = self.display,
onerror = onerror,
drawable = self.id,
gc = gc,
x = x,
y = y,
items = items)
def poly_text_16(self, gc, x, y, items, onerror = None):
request.PolyText16(display = self.display,
onerror = onerror,
drawable = self.id,
gc = gc,
x = x,
y = y,
items = items)
def image_text(self, gc, x, y, string, onerror = None):
request.ImageText8(display = self.display,
onerror = onerror,
drawable = self.id,
gc = gc,
x = x,
y = y,
string = string)
def image_text_16(self, gc, x, y, string, onerror = None):
request.ImageText16(display = self.display,
onerror = onerror,
drawable = self.id,
gc = gc,
x = x,
y = y,
string = string)
def query_best_size(self, item_class, width, height):
return request.QueryBestSize(display = self.display,
item_class = item_class,
drawable = self.id,
width = width,
height = height)
class Window(Drawable):
__window__ = resource.Resource.__resource__
_STRING_ENCODING = 'ISO-8859-1'
_UTF8_STRING_ENCODING = 'UTF-8'
def create_window(self, x, y, width, height, border_width, depth,
window_class = X.CopyFromParent,
visual = X.CopyFromParent,
onerror = None,
**keys):
wid = self.display.allocate_resource_id()
request.CreateWindow(display = self.display,
onerror = onerror,
depth = depth,
wid = wid,
parent = self.id,
x = x,
y = y,
width = width,
height = height,
border_width = border_width,
window_class = window_class,
visual = visual,
attrs = keys)
cls = self.display.get_resource_class('window', Window)
return cls(self.display, wid, owner = 1)
def change_attributes(self, onerror = None, **keys):
request.ChangeWindowAttributes(display = self.display,
onerror = onerror,
window = self.id,
attrs = keys)
def get_attributes(self):
return request.GetWindowAttributes(display = self.display,
window = self.id)
def destroy(self, onerror = None):
request.DestroyWindow(display = self.display,
onerror = onerror,
window = self.id)
self.display.free_resource_id(self.id)
def destroy_sub_windows(self, onerror = None):
request.DestroySubWindows(display = self.display,
onerror = onerror,
window = self.id)
def change_save_set(self, mode, onerror = None):
request.ChangeSaveSet(display = self.display,
onerror = onerror,
mode = mode,
window = self.id)
def reparent(self, parent, x, y, onerror = None):
request.ReparentWindow(display = self.display,
onerror = onerror,
window = self.id,
parent = parent,
x = x,
y = y)
def map(self, onerror = None):
request.MapWindow(display = self.display,
onerror = onerror,
window = self.id)
def map_sub_windows(self, onerror = None):
request.MapSubwindows(display = self.display,
onerror = onerror,
window = self.id)
def unmap(self, onerror = None):
request.UnmapWindow(display = self.display,
onerror = onerror,
window = self.id)
def unmap_sub_windows(self, onerror = None):
request.UnmapSubwindows(display = self.display,
onerror = onerror,
window = self.id)
def configure(self, onerror = None, **keys):
request.ConfigureWindow(display = self.display,
onerror = onerror,
window = self.id,
attrs = keys)
def circulate(self, direction, onerror = None):
request.CirculateWindow(display = self.display,
onerror = onerror,
direction = direction,
window = self.id)
def raise_window(self, onerror = None):
"""alias for raising the window to the top - as in XRaiseWindow"""
self.configure(onerror, stack_mode = X.Above)
def query_tree(self):
return request.QueryTree(display = self.display,
window = self.id)
def change_property(self, property, property_type, format, data,
mode = X.PropModeReplace, onerror = None):
request.ChangeProperty(display = self.display,
onerror = onerror,
mode = mode,
window = self.id,
property = property,
type = property_type,
data = (format, data))
def change_text_property(self, property, property_type, data,
mode = X.PropModeReplace, onerror = None):
if not isinstance(data, bytes):
if property_type == Xatom.STRING:
data = data.encode(self._STRING_ENCODING)
elif property_type == self.display.get_atom('UTF8_STRING'):
data = data.encode(self._UTF8_STRING_ENCODING)
self.change_property(property, property_type, 8, data,
mode=mode, onerror=onerror)
def delete_property(self, property, onerror = None):
request.DeleteProperty(display = self.display,
onerror = onerror,
window = self.id,
property = property)
def get_property(self, property, property_type, offset, length, delete = 0):
r = request.GetProperty(display = self.display,
delete = delete,
window = self.id,
property = property,
type = property_type,
long_offset = offset,
long_length = length)
if r.property_type:
fmt, value = r.value
r.format = fmt
r.value = value
return r
else:
return None
def get_full_property(self, property, property_type, sizehint = 10):
prop = self.get_property(property, property_type, 0, sizehint)
if prop:
val = prop.value
if prop.bytes_after:
prop = self.get_property(property, property_type, sizehint,
prop.bytes_after // 4 + 1)
val = val + prop.value
prop.value = val
return prop
else:
return None
def get_full_text_property(self, property, property_type=X.AnyPropertyType, sizehint = 10):
prop = self.get_full_property(property, property_type,
sizehint=sizehint)
if prop is None or prop.format != 8:
return None
if prop.property_type == Xatom.STRING:
prop.value = prop.value.decode(self._STRING_ENCODING)
elif prop.property_type == self.display.get_atom('UTF8_STRING'):
prop.value = prop.value.decode(self._UTF8_STRING_ENCODING)
# FIXME: at least basic support for compound text would be nice.
# elif prop.property_type == self.display.get_atom('COMPOUND_TEXT'):
return prop.value
def list_properties(self):
r = request.ListProperties(display = self.display,
window = self.id)
return r.atoms
def set_selection_owner(self, selection, time, onerror = None):
request.SetSelectionOwner(display = self.display,
onerror = onerror,
window = self.id,
selection = selection,
time = time)
def convert_selection(self, selection, target, property, time, onerror = None):
request.ConvertSelection(display = self.display,
onerror = onerror,
requestor = self.id,
selection = selection,
target = target,
property = property,
time = time)
def send_event(self, event, event_mask = 0, propagate = 0, onerror = None):
request.SendEvent(display = self.display,
onerror = onerror,
propagate = propagate,
destination = self.id,
event_mask = event_mask,
event = event)
def grab_pointer(self, owner_events, event_mask,
pointer_mode, keyboard_mode,
confine_to, cursor, time):
r = request.GrabPointer(display = self.display,
owner_events = owner_events,
grab_window = self.id,
event_mask = event_mask,
pointer_mode = pointer_mode,
keyboard_mode = keyboard_mode,
confine_to = confine_to,
cursor = cursor,
time = time)
return r.status
def grab_button(self, button, modifiers, owner_events, event_mask,
pointer_mode, keyboard_mode,
confine_to, cursor, onerror = None):
request.GrabButton(display = self.display,
onerror = onerror,
owner_events = owner_events,
grab_window = self.id,
event_mask = event_mask,
pointer_mode = pointer_mode,
keyboard_mode = keyboard_mode,
confine_to = confine_to,
cursor = cursor,
button = button,
modifiers = modifiers)
def ungrab_button(self, button, modifiers, onerror = None):
request.UngrabButton(display = self.display,
onerror = onerror,
button = button,
grab_window = self.id,
modifiers = modifiers)
def grab_keyboard(self, owner_events, pointer_mode, keyboard_mode, time):
r = request.GrabKeyboard(display = self.display,
owner_events = owner_events,
grab_window = self.id,
time = time,
pointer_mode = pointer_mode,
keyboard_mode = keyboard_mode)
return r.status
def grab_key(self, key, modifiers, owner_events, pointer_mode, keyboard_mode, onerror = None):
request.GrabKey(display = self.display,
onerror = onerror,
owner_events = owner_events,
grab_window = self.id,
modifiers = modifiers,
key = key,
pointer_mode = pointer_mode,
keyboard_mode = keyboard_mode)
def ungrab_key(self, key, modifiers, onerror = None):
request.UngrabKey(display = self.display,
onerror = onerror,
key = key,
grab_window = self.id,
modifiers = modifiers)
def query_pointer(self):
return request.QueryPointer(display = self.display,
window = self.id)
def get_motion_events(self, start, stop):
r = request.GetMotionEvents(display = self.display,
window = self.id,
start = start,
stop = stop)
return r.events
def translate_coords(self, src_window, src_x, src_y):
return request.TranslateCoords(display = self.display,
src_wid = src_window,
dst_wid = self.id,
src_x = src_x,
src_y = src_y)
def warp_pointer(self, x, y, src_window = 0, src_x = 0, src_y = 0,
src_width = 0, src_height = 0, onerror = None):
request.WarpPointer(display = self.display,
onerror = onerror,
src_window = src_window,
dst_window = self.id,
src_x = src_x,
src_y = src_y,
src_width = src_width,
src_height = src_height,
dst_x = x,
dst_y = y)
def set_input_focus(self, revert_to, time, onerror = None):
request.SetInputFocus(display = self.display,
onerror = onerror,
revert_to = revert_to,
focus = self.id,
time = time)
def clear_area(self, x = 0, y = 0, width = 0, height = 0, exposures = 0, onerror = None):
request.ClearArea(display = self.display,
onerror = onerror,
exposures = exposures,
window = self.id,
x = x,
y = y,
width = width,
height = height)
def create_colormap(self, visual, alloc):
mid = self.display.allocate_resource_id()
request.CreateColormap(display = self.display,
alloc = alloc,
mid = mid,
window = self.id,
visual = visual)
cls = self.display.get_resource_class('colormap', colormap.Colormap)
return cls(self.display, mid, owner = 1)
def list_installed_colormaps(self):
r = request.ListInstalledColormaps(display = self.display,
window = self.id)
return r.cmaps
def rotate_properties(self, properties, delta, onerror = None):
request.RotateProperties(display = self.display,
onerror = onerror,
window = self.id,
delta = delta,
properties = properties)
def set_wm_name(self, name, onerror = None):
self.change_text_property(Xatom.WM_NAME, Xatom.STRING, name,
onerror = onerror)
def get_wm_name(self):
return self.get_full_text_property(Xatom.WM_NAME, Xatom.STRING)
def set_wm_icon_name(self, name, onerror = None):
self.change_text_property(Xatom.WM_ICON_NAME, Xatom.STRING, name,
onerror = onerror)
def get_wm_icon_name(self):
return self.get_full_text_property(Xatom.WM_ICON_NAME, Xatom.STRING)
def set_wm_class(self, inst, cls, onerror = None):
self.change_text_property(Xatom.WM_CLASS, Xatom.STRING,
'%s\0%s\0' % (inst, cls),
onerror = onerror)
def get_wm_class(self):
value = self.get_full_text_property(Xatom.WM_CLASS, Xatom.STRING)
if value is None:
return None
parts = value.split('\0')
if len(parts) < 2:
return None
else:
return parts[0], parts[1]
def set_wm_transient_for(self, window, onerror = None):
self.change_property(Xatom.WM_TRANSIENT_FOR, Xatom.WINDOW,
32, [window.id],
onerror = onerror)
def get_wm_transient_for(self):
d = self.get_property(Xatom.WM_TRANSIENT_FOR, Xatom.WINDOW, 0, 1)
if d is None or d.format != 32 or len(d.value) < 1:
return None
else:
cls = self.display.get_resource_class('window', Window)
return cls(self.display, d.value[0])
def set_wm_protocols(self, protocols, onerror = None):
self.change_property(self.display.get_atom('WM_PROTOCOLS'),
Xatom.ATOM, 32, protocols,
onerror = onerror)
def get_wm_protocols(self):
d = self.get_full_property(self.display.get_atom('WM_PROTOCOLS'), Xatom.ATOM)
if d is None or d.format != 32:
return []
else:
return d.value
def set_wm_colormap_windows(self, windows, onerror = None):
self.change_property(self.display.get_atom('WM_COLORMAP_WINDOWS'),
Xatom.WINDOW, 32,
map(lambda w: w.id, windows),
onerror = onerror)
def get_wm_colormap_windows(self):
d = self.get_full_property(self.display.get_atom('WM_COLORMAP_WINDOWS'),
Xatom.WINDOW)
if d is None or d.format != 32:
return []
else:
cls = self.display.get_resource_class('window', Window)
return map(lambda i, d = self.display, c = cls: c(d, i),
d.value)
def set_wm_client_machine(self, name, onerror = None):
self.change_text_property(Xatom.WM_CLIENT_MACHINE, Xatom.STRING, name,
onerror = onerror)
def get_wm_client_machine(self):
return self.get_full_text_property(Xatom.WM_CLIENT_MACHINE, Xatom.STRING)
def set_wm_normal_hints(self, hints = {}, onerror = None, **keys):
self._set_struct_prop(Xatom.WM_NORMAL_HINTS, Xatom.WM_SIZE_HINTS,
icccm.WMNormalHints, hints, keys, onerror)
def get_wm_normal_hints(self):
return self._get_struct_prop(Xatom.WM_NORMAL_HINTS, Xatom.WM_SIZE_HINTS,
icccm.WMNormalHints)
def set_wm_hints(self, hints = {}, onerror = None, **keys):
self._set_struct_prop(Xatom.WM_HINTS, Xatom.WM_HINTS,
icccm.WMHints, hints, keys, onerror)
def get_wm_hints(self):
return self._get_struct_prop(Xatom.WM_HINTS, Xatom.WM_HINTS,
icccm.WMHints)
def set_wm_state(self, hints = {}, onerror = None, **keys):
atom = self.display.get_atom('WM_STATE')
self._set_struct_prop(atom, atom, icccm.WMState, hints, keys, onerror)
def get_wm_state(self):
atom = self.display.get_atom('WM_STATE')
return self._get_struct_prop(atom, atom, icccm.WMState)
def set_wm_icon_size(self, hints = {}, onerror = None, **keys):
self._set_struct_prop(Xatom.WM_ICON_SIZE, Xatom.WM_ICON_SIZE,
icccm.WMIconSize, hints, keys, onerror)
def get_wm_icon_size(self):
return self._get_struct_prop(Xatom.WM_ICON_SIZE, Xatom.WM_ICON_SIZE,
icccm.WMIconSize)
# Helper function for getting structured properties.
# pname and ptype are atoms, and pstruct is a Struct object.
# Returns a DictWrapper, or None
def _get_struct_prop(self, pname, ptype, pstruct):
r = self.get_property(pname, ptype, 0, pstruct.static_size // 4)
if r and r.format == 32:
value = rq.encode_array(r.value)
if len(value) == pstruct.static_size:
return pstruct.parse_binary(value, self.display)[0]
return None
# Helper function for setting structured properties.
# pname and ptype are atoms, and pstruct is a Struct object.
# hints is a mapping or a DictWrapper, keys is a mapping. keys
# will be modified. onerror is the error handler.
def _set_struct_prop(self, pname, ptype, pstruct, hints, keys, onerror):
if isinstance(hints, rq.DictWrapper):
keys.update(hints._data)
else:
keys.update(hints)
value = pstruct.to_binary(*(), **keys)
self.change_property(pname, ptype, 32, value, onerror = onerror)
class Pixmap(Drawable):
__pixmap__ = resource.Resource.__resource__
def free(self, onerror = None):
request.FreePixmap(display = self.display,
onerror = onerror,
pixmap = self.id)
self.display.free_resource_id(self.id)
def create_cursor(self, mask, foreground, background, x, y):
fore_red, fore_green, fore_blue = foreground
back_red, back_green, back_blue = background
cid = self.display.allocate_resource_id()
request.CreateCursor(display = self.display,
cid = cid,
source = self.id,
mask = mask,
fore_red = fore_red,
fore_green = fore_green,
fore_blue = fore_blue,
back_red = back_red,
back_green = back_green,
back_blue = back_blue,
x = x,
y = y)
cls = self.display.get_resource_class('cursor', cursor.Cursor)
return cls(self.display, cid, owner = 1)
def roundup(value, unit):
return (value + (unit - 1)) & ~(unit - 1)