Spaces:
Runtime error
Runtime error
# -*- coding: utf-8 -*- | |
# | |
# Xlib.protocol.display -- core display communication | |
# | |
# Copyright (C) 2000-2002 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 | |
# Standard modules | |
import errno | |
import math | |
import select | |
import socket | |
import struct | |
import sys | |
# Python 2/3 compatibility. | |
from six import PY3, byte2int, indexbytes | |
# Xlib modules | |
from .. import error | |
from ..ext import ge | |
from ..support import lock, connect | |
# Xlib.protocol modules | |
from . import rq | |
from . import event | |
if PY3: | |
class bytesview(object): | |
def __init__(self, data, offset=0, size=None): | |
if size is None: | |
size = len(data)-offset | |
if isinstance(data, bytes): | |
view = memoryview(data) | |
elif isinstance(data, bytesview): | |
view = data.view | |
else: | |
raise TypeError('unsupported type: {}'.format(type(data))) | |
self.view = view[offset:offset+size] | |
def __len__(self): | |
return len(self.view) | |
def __getitem__(self, key): | |
if isinstance(key, slice): | |
return bytes(self.view[key]) | |
return self.view[key] | |
else: | |
def bytesview(data, offset=0, size=None): | |
if not isinstance(data, (bytes, buffer)): | |
raise TypeError('unsupported type: {}'.format(type(data))) | |
if size is None: | |
size = len(data)-offset | |
return buffer(data, offset, size) | |
class Display(object): | |
extension_major_opcodes = {} | |
error_classes = error.xerror_class.copy() | |
event_classes = event.event_class.copy() | |
def __init__(self, display = None): | |
name, protocol, host, displayno, screenno = connect.get_display(display) | |
self.display_name = name | |
self.default_screen = screenno | |
self.socket = connect.get_socket(name, protocol, host, displayno) | |
auth_name, auth_data = connect.get_auth(self.socket, name, | |
protocol, host, displayno) | |
# Internal structures for communication, grouped | |
# by their function and locks | |
# Socket error indicator, set when the socket is closed | |
# in one way or another | |
self.socket_error_lock = lock.allocate_lock() | |
self.socket_error = None | |
# Event queue | |
self.event_queue_read_lock = lock.allocate_lock() | |
self.event_queue_write_lock = lock.allocate_lock() | |
self.event_queue = [] | |
# Unsent request queue and sequence number counter | |
self.request_queue_lock = lock.allocate_lock() | |
self.request_serial = 1 | |
self.request_queue = [] | |
# Send-and-receive loop, see function send_and_receive | |
# for a detailed explanation | |
self.send_recv_lock = lock.allocate_lock() | |
self.send_active = 0 | |
self.recv_active = 0 | |
self.event_waiting = 0 | |
self.event_wait_lock = lock.allocate_lock() | |
self.request_waiting = 0 | |
self.request_wait_lock = lock.allocate_lock() | |
# Calculate optimal default buffer size for recv. | |
buffer_size = self.socket.getsockopt(socket.SOL_SOCKET, | |
socket.SO_RCVBUF) | |
buffer_size = math.pow(2, math.floor(math.log(buffer_size, 2))) | |
self.recv_buffer_size = int(buffer_size) | |
# Data used by the send-and-receive loop | |
self.sent_requests = [] | |
self.recv_packet_len = 0 | |
self.data_send = b'' | |
self.data_recv = b'' | |
self.data_sent_bytes = 0 | |
# Resource ID structures | |
self.resource_id_lock = lock.allocate_lock() | |
self.resource_ids = {} | |
self.last_resource_id = 0 | |
# Use an default error handler, one which just prints the error | |
self.error_handler = None | |
# Right, now we're all set up for the connection setup | |
# request with the server. | |
# Figure out which endianness the hardware uses | |
self.big_endian = struct.unpack('BB', struct.pack('H', 0x0100))[0] | |
if self.big_endian: | |
order = 0x42 | |
else: | |
order = 0x6c | |
# Send connection setup | |
r = ConnectionSetupRequest(self, | |
byte_order = order, | |
protocol_major = 11, | |
protocol_minor = 0, | |
auth_prot_name = auth_name, | |
auth_prot_data = auth_data) | |
# Did connection fail? | |
if r.status != 1: | |
raise error.DisplayConnectionError(self.display_name, r.reason) | |
# Set up remaining info | |
self.info = r | |
self.default_screen = min(self.default_screen, len(self.info.roots) - 1) | |
# | |
# Public interface | |
# | |
def get_display_name(self): | |
return self.display_name | |
def get_default_screen(self): | |
return self.default_screen | |
def fileno(self): | |
self.check_for_error() | |
return self.socket.fileno() | |
def next_event(self): | |
self.check_for_error() | |
# Main lock, so that only one thread at a time performs the | |
# event waiting code. This at least guarantees that the first | |
# thread calling next_event() will get the next event, although | |
# no order is guaranteed among other threads calling next_event() | |
# while the first is blocking. | |
self.event_queue_read_lock.acquire() | |
# Lock event queue, so we can check if it is empty | |
self.event_queue_write_lock.acquire() | |
# We have too loop until we get an event, as | |
# we might be woken up when there is no event. | |
while not self.event_queue: | |
# Lock send_recv so no send_and_receive | |
# can start or stop while we're checking | |
# whether there are one active. | |
self.send_recv_lock.acquire() | |
# Release event queue to allow an send_and_recv to | |
# insert any now. | |
self.event_queue_write_lock.release() | |
# Call send_and_recv, which will return when | |
# something has occured | |
self.send_and_recv(event = True) | |
# Before looping around, lock the event queue against | |
# modifications. | |
self.event_queue_write_lock.acquire() | |
# Whiew, we have an event! Remove it from | |
# the event queue and relaese its write lock. | |
event = self.event_queue[0] | |
del self.event_queue[0] | |
self.event_queue_write_lock.release() | |
# Finally, allow any other threads which have called next_event() | |
# while we were waiting to proceed. | |
self.event_queue_read_lock.release() | |
# And return the event! | |
return event | |
def pending_events(self): | |
self.check_for_error() | |
# Make a send_and_recv pass, receiving any events | |
self.send_recv_lock.acquire() | |
self.send_and_recv(recv = True) | |
# Lock the queue, get the event count, and unlock again. | |
self.event_queue_write_lock.acquire() | |
count = len(self.event_queue) | |
self.event_queue_write_lock.release() | |
return count | |
def flush(self): | |
self.check_for_error() | |
self.send_recv_lock.acquire() | |
self.send_and_recv(flush = True) | |
def close(self): | |
self.flush() | |
self.close_internal('client') | |
def set_error_handler(self, handler): | |
self.error_handler = handler | |
def allocate_resource_id(self): | |
"""id = d.allocate_resource_id() | |
Allocate a new X resource id number ID. | |
Raises ResourceIDError if there are no free resource ids. | |
""" | |
self.resource_id_lock.acquire() | |
try: | |
i = self.last_resource_id | |
while i in self.resource_ids: | |
i = i + 1 | |
if i > self.info.resource_id_mask: | |
i = 0 | |
if i == self.last_resource_id: | |
raise error.ResourceIDError('out of resource ids') | |
self.resource_ids[i] = None | |
self.last_resource_id = i | |
return self.info.resource_id_base | i | |
finally: | |
self.resource_id_lock.release() | |
def free_resource_id(self, rid): | |
"""d.free_resource_id(rid) | |
Free resource id RID. Attempts to free a resource id which | |
isn't allocated by us are ignored. | |
""" | |
self.resource_id_lock.acquire() | |
try: | |
i = rid & self.info.resource_id_mask | |
# Attempting to free a resource id outside our range | |
if rid - i != self.info.resource_id_base: | |
return None | |
try: | |
del self.resource_ids[i] | |
except KeyError: | |
pass | |
finally: | |
self.resource_id_lock.release() | |
def get_resource_class(self, class_name, default = None): | |
"""class = d.get_resource_class(class_name, default = None) | |
Return the class to be used for X resource objects of type | |
CLASS_NAME, or DEFAULT if no such class is set. | |
""" | |
return self.resource_classes.get(class_name, default) | |
def set_extension_major(self, extname, major): | |
self.extension_major_opcodes[extname] = major | |
def get_extension_major(self, extname): | |
return self.extension_major_opcodes[extname] | |
def add_extension_event(self, code, evt, subcode=None): | |
if subcode == None: | |
self.event_classes[code] = evt | |
else: | |
if not code in self.event_classes: | |
self.event_classes[code] = {subcode: evt} | |
else: | |
self.event_classes[code][subcode] = evt | |
def add_extension_error(self, code, err): | |
self.error_classes[code] = err | |
# | |
# Private functions | |
# | |
def check_for_error(self): | |
self.socket_error_lock.acquire() | |
err = self.socket_error | |
self.socket_error_lock.release() | |
if err: | |
raise err | |
def send_request(self, request, wait_for_response): | |
if self.socket_error: | |
raise self.socket_error | |
self.request_queue_lock.acquire() | |
request._serial = self.request_serial | |
self.request_serial = (self.request_serial + 1) % 65536 | |
self.request_queue.append((request, wait_for_response)) | |
qlen = len(self.request_queue) | |
self.request_queue_lock.release() | |
# if qlen > 10: | |
# self.flush() | |
def close_internal(self, whom): | |
# Clear out data structures | |
self.request_queue = None | |
self.sent_requests = None | |
self.event_queue = None | |
self.data_send = None | |
self.data_recv = None | |
# Close the connection | |
self.socket.close() | |
# Set a connection closed indicator | |
self.socket_error_lock.acquire() | |
self.socket_error = error.ConnectionClosedError(whom) | |
self.socket_error_lock.release() | |
def send_and_recv(self, flush = False, event = False, request = None, recv = False): | |
"""send_and_recv(flush = None, event = None, request = None, recv = None) | |
Perform I/O, or wait for some other thread to do it for us. | |
send_recv_lock MUST be LOCKED when send_and_recv is called. | |
It will be UNLOCKED at return. | |
Exactly or one of the parameters flush, event, request and recv must | |
be set to control the return condition. | |
To attempt to send all requests in the queue, flush should | |
be true. Will return immediately if another thread is | |
already doing send_and_recv. | |
To wait for an event to be received, event should be true. | |
To wait for a response to a certain request (either an error | |
or a response), request should be set to that request's | |
serial number. | |
To just read any pending data from the server, recv should be true. | |
It is not guaranteed that the return condition has been | |
fulfilled when the function returns, so the caller has to loop | |
until it is finished. | |
""" | |
# We go to sleep if there is already a thread doing what we | |
# want to do: | |
# If flushing, we want to send | |
# If waiting for a response to a request, we want to send | |
# (to ensure that the request was sent - we alway recv | |
# when we get to the main loop, but sending is the important | |
# thing here) | |
# If waiting for an event, we want to recv | |
# If just trying to receive anything we can, we want to recv | |
# FIXME: It would be good if we could also sleep when we're waiting on | |
# a response to a request that has already been sent. | |
if (((flush or request is not None) and self.send_active) | |
or ((event or recv) and self.recv_active)): | |
# Signal that we are waiting for something. These locks | |
# together with the *_waiting variables are used as | |
# semaphores. When an event or a request response arrives, | |
# it will zero the *_waiting and unlock the lock. The | |
# locks will also be unlocked when an active send_and_recv | |
# finishes to signal the other waiting threads that one of | |
# them has to take over the send_and_recv function. | |
# All this makes these locks and variables a part of the | |
# send_and_recv control logic, and hence must be modified | |
# only when we have the send_recv_lock locked. | |
if event: | |
wait_lock = self.event_wait_lock | |
if not self.event_waiting: | |
self.event_waiting = 1 | |
wait_lock.acquire() | |
elif request is not None: | |
wait_lock = self.request_wait_lock | |
if not self.request_waiting: | |
self.request_waiting = 1 | |
wait_lock.acquire() | |
# Release send_recv, allowing a send_and_recive | |
# to terminate or other threads to queue up | |
self.send_recv_lock.release() | |
# Return immediately if flushing, even if that | |
# might mean that not necessarily all requests | |
# have been sent. | |
if flush or recv: | |
return | |
# Wait for something to happen, as the wait locks are | |
# unlocked either when what we wait for has arrived (not | |
# necessarily the exact object we're waiting for, though), | |
# or when an active send_and_recv exits. | |
# Release it immediately afterwards as we're only using | |
# the lock for synchonization. Since we're not modifying | |
# event_waiting or request_waiting here we don't have | |
# to lock send_and_recv_lock. In fact, we can't do that | |
# or we trigger a dead-lock. | |
wait_lock.acquire() | |
wait_lock.release() | |
# Return to caller to let it check whether it has | |
# got the data it was waiting for | |
return | |
# There's no thread doing what we need to do. Find out exactly | |
# what to do | |
# There must always be some thread receiving data, but it must not | |
# necessarily be us | |
if not self.recv_active: | |
receiving = 1 | |
self.recv_active = 1 | |
else: | |
receiving = 0 | |
flush_bytes = None | |
sending = 0 | |
# Loop, receiving and sending data. | |
while 1: | |
# We might want to start sending data | |
if sending or not self.send_active: | |
# Turn all requests on request queue into binary form | |
# and append them to self.data_send | |
self.request_queue_lock.acquire() | |
for req, wait in self.request_queue: | |
self.data_send = self.data_send + req._binary | |
if wait: | |
self.sent_requests.append(req) | |
del self.request_queue[:] | |
self.request_queue_lock.release() | |
# If there now is data to send, mark us as senders | |
if self.data_send: | |
self.send_active = 1 | |
sending = 1 | |
else: | |
self.send_active = 0 | |
sending = 0 | |
# We've done all setup, so release the lock and start waiting | |
# for the network to fire up | |
self.send_recv_lock.release() | |
# There's no longer anything useful we can do here. | |
if not (sending or receiving): | |
break | |
# If we're flushing, figure out how many bytes we | |
# have to send so that we're not caught in an interminable | |
# loop if other threads continuously append requests. | |
if flush and flush_bytes is None: | |
flush_bytes = self.data_sent_bytes + len(self.data_send) | |
try: | |
# We're only checking for the socket to be writable | |
# if we're the sending thread. We always check for it | |
# to become readable: either we are the receiving thread | |
# and should take care of the data, or the receiving thread | |
# might finish receiving after having read the data | |
if sending: | |
writeset = [self.socket] | |
else: | |
writeset = [] | |
# Timeout immediately if we're only checking for | |
# something to read or if we're flushing, otherwise block | |
if recv or flush: | |
timeout = 0 | |
else: | |
timeout = None | |
rs, ws, es = select.select([self.socket], writeset, [], timeout) | |
# Ignore errors caused by a signal received while blocking. | |
# All other errors are re-raised. | |
except select.error as err: | |
if isinstance(err, OSError): | |
code = err.errno | |
else: | |
code = err[0] | |
if code != errno.EINTR: | |
raise | |
# We must lock send_and_recv before we can loop to | |
# the start of the loop | |
self.send_recv_lock.acquire() | |
continue | |
# Socket is ready for sending data, send as much as possible. | |
if ws: | |
try: | |
i = self.socket.send(self.data_send) | |
except socket.error as err: | |
self.close_internal('server: %s' % err) | |
raise self.socket_error | |
self.data_send = self.data_send[i:] | |
self.data_sent_bytes = self.data_sent_bytes + i | |
# There is data to read | |
gotreq = 0 | |
if rs: | |
# We're the receiving thread, parse the data | |
if receiving: | |
try: | |
count = self.recv_packet_len - len(self.data_recv) | |
count = max(self.recv_buffer_size, count) | |
bytes_recv = self.socket.recv(count) | |
except socket.error as err: | |
self.close_internal('server: %s' % err) | |
raise self.socket_error | |
if not bytes_recv: | |
# Clear up, set a connection closed indicator and raise it | |
self.close_internal('server') | |
raise self.socket_error | |
self.data_recv = bytes(self.data_recv) + bytes_recv | |
gotreq = self.parse_response(request) | |
# Otherwise return, allowing the calling thread to figure | |
# out if it has got the data it needs | |
else: | |
# We must be a sending thread if we're here, so reset | |
# that indicator. | |
self.send_recv_lock.acquire() | |
self.send_active = 0 | |
self.send_recv_lock.release() | |
# And return to the caller | |
return | |
# There are three different end of send-recv-loop conditions. | |
# However, we don't leave the loop immediately, instead we | |
# try to send and receive any data that might be left. We | |
# do this by giving a timeout of 0 to select to poll | |
# the socket. | |
# When flushing: all requests have been sent | |
if flush and flush_bytes >= self.data_sent_bytes: | |
break | |
# When waiting for an event: an event has been read | |
if event and self.event_queue: | |
break | |
# When processing a certain request: got its reply | |
if request is not None and gotreq: | |
break | |
# Always break if we just want to receive as much as possible | |
if recv: | |
break | |
# Else there's may still data which must be sent, or | |
# we haven't got the data we waited for. Lock and loop | |
self.send_recv_lock.acquire() | |
# We have accomplished the callers request. | |
# Record that there are now no active send_and_recv, | |
# and wake up all waiting thread | |
self.send_recv_lock.acquire() | |
if sending: | |
self.send_active = 0 | |
if receiving: | |
self.recv_active = 0 | |
if self.event_waiting: | |
self.event_waiting = 0 | |
self.event_wait_lock.release() | |
if self.request_waiting: | |
self.request_waiting = 0 | |
self.request_wait_lock.release() | |
self.send_recv_lock.release() | |
def parse_response(self, request): | |
"""Internal method. | |
Parse data received from server. If REQUEST is not None | |
true is returned if the request with that serial number | |
was received, otherwise false is returned. | |
If REQUEST is -1, we're parsing the server connection setup | |
response. | |
""" | |
if request == -1: | |
return self.parse_connection_setup() | |
# Parse ordinary server response | |
gotreq = False | |
while True: | |
if self.data_recv: | |
# Check the first byte to find out what kind of response it is | |
rtype = byte2int(self.data_recv) | |
# Are we're waiting for additional data for the current packet? | |
if self.recv_packet_len: | |
if len(self.data_recv) < self.recv_packet_len: | |
return gotreq | |
if rtype == 1: | |
gotreq = self.parse_request_response(request) or gotreq | |
continue | |
elif rtype & 0x7f == ge.GenericEventCode: | |
self.parse_event_response(rtype) | |
continue | |
else: | |
raise AssertionError(rtype) | |
# Every response is at least 32 bytes long, so don't bother | |
# until we have received that much | |
if len(self.data_recv) < 32: | |
return gotreq | |
# Error response | |
if rtype == 0: | |
gotreq = self.parse_error_response(request) or gotreq | |
# Request response or generic event. | |
elif rtype == 1 or rtype & 0x7f == ge.GenericEventCode: | |
# Set reply length, and loop around to see if | |
# we have got the full response | |
rlen = int(struct.unpack('=L', self.data_recv[4:8])[0]) | |
self.recv_packet_len = 32 + rlen * 4 | |
# Else non-generic event | |
else: | |
self.parse_event_response(rtype) | |
def parse_error_response(self, request): | |
# Code is second byte | |
code = indexbytes(self.data_recv, 1) | |
# Fetch error class | |
estruct = self.error_classes.get(code, error.XError) | |
e = estruct(self, self.data_recv[:32]) | |
self.data_recv = bytesview(self.data_recv, 32) | |
# print 'recv Error:', e | |
req = self.get_waiting_request(e.sequence_number) | |
# Error for a request whose response we are waiting for, | |
# or which have an error handler. However, if the error | |
# handler indicates that it hasn't taken care of the | |
# error, pass it on to the default error handler | |
if req and req._set_error(e): | |
# If this was a ReplyRequest, unlock any threads waiting | |
# for a request to finish | |
if isinstance(req, rq.ReplyRequest): | |
self.send_recv_lock.acquire() | |
if self.request_waiting: | |
self.request_waiting = 0 | |
self.request_wait_lock.release() | |
self.send_recv_lock.release() | |
return request == e.sequence_number | |
# Else call the error handler | |
else: | |
if self.error_handler: | |
rq.call_error_handler(self.error_handler, e, None) | |
else: | |
self.default_error_handler(e) | |
return False | |
def default_error_handler(self, err): | |
sys.stderr.write('X protocol error:\n%s\n' % err) | |
def parse_request_response(self, request): | |
req = self.get_waiting_replyrequest() | |
# Sequence number is always data[2:4] | |
# Do sanity check before trying to parse the data | |
sno = struct.unpack('=H', self.data_recv[2:4])[0] | |
if sno != req._serial: | |
raise RuntimeError("Expected reply for request %s, but got %s. Can't happen!" | |
% (req._serial, sno)) | |
req._parse_response(self.data_recv[:self.recv_packet_len]) | |
# print 'recv Request:', req | |
self.data_recv = bytesview(self.data_recv, self.recv_packet_len) | |
self.recv_packet_len = 0 | |
# Unlock any response waiting threads | |
self.send_recv_lock.acquire() | |
if self.request_waiting: | |
self.request_waiting = 0 | |
self.request_wait_lock.release() | |
self.send_recv_lock.release() | |
return req.sequence_number == request | |
def parse_event_response(self, etype): | |
# Skip bit 8, that is set if this event came from an SendEvent | |
etype = etype & 0x7f | |
if etype == ge.GenericEventCode: | |
length = self.recv_packet_len | |
else: | |
length = 32 | |
estruct = self.event_classes.get(etype, event.AnyEvent) | |
if type(estruct) == dict: | |
subcode = self.data_recv[1] | |
# Python2 compatibility | |
if type(subcode) == str: | |
subcode = ord(subcode) | |
# this etype refers to a set of sub-events with individual subcodes | |
estruct = estruct[subcode] | |
e = estruct(display = self, binarydata = self.data_recv[:length]) | |
if etype == ge.GenericEventCode: | |
self.recv_packet_len = 0 | |
self.data_recv = bytesview(self.data_recv, length) | |
# Drop all requests having an error handler, | |
# but which obviously succeded. | |
# Decrement it by one, so that we don't remove the request | |
# that generated these events, if there is such a one. | |
# Bug reported by Ilpo Nyyssönen | |
# Note: not all events have a sequence_number field! | |
# (e.g. KeymapNotify). | |
if hasattr(e, 'sequence_number'): | |
self.get_waiting_request((e.sequence_number - 1) % 65536) | |
# print 'recv Event:', e | |
# Insert the event into the queue | |
self.event_queue_write_lock.acquire() | |
self.event_queue.append(e) | |
self.event_queue_write_lock.release() | |
# Unlock any event waiting threads | |
self.send_recv_lock.acquire() | |
if self.event_waiting: | |
self.event_waiting = 0 | |
self.event_wait_lock.release() | |
self.send_recv_lock.release() | |
def get_waiting_request(self, sno): | |
if not self.sent_requests: | |
return None | |
# Normalize sequence numbers, even if they have wrapped. | |
# This ensures that | |
# sno <= last_serial | |
# and | |
# self.sent_requests[0]._serial <= last_serial | |
if self.sent_requests[0]._serial > self.request_serial: | |
last_serial = self.request_serial + 65536 | |
if sno < self.request_serial: | |
sno = sno + 65536 | |
else: | |
last_serial = self.request_serial | |
if sno > self.request_serial: | |
sno = sno - 65536 | |
# No matching events at all | |
if sno < self.sent_requests[0]._serial: | |
return None | |
# Find last req <= sno | |
req = None | |
reqpos = len(self.sent_requests) | |
adj = 0 | |
last = 0 | |
for i in range(0, len(self.sent_requests)): | |
rno = self.sent_requests[i]._serial + adj | |
# Did serial numbers just wrap around? | |
if rno < last: | |
adj = 65536 | |
rno = rno + adj | |
last = rno | |
if sno == rno: | |
req = self.sent_requests[i] | |
reqpos = i + 1 | |
break | |
elif sno < rno: | |
req = None | |
reqpos = i | |
break | |
# Delete all request such as req <= sno | |
del self.sent_requests[:reqpos] | |
return req | |
def get_waiting_replyrequest(self): | |
for i in range(0, len(self.sent_requests)): | |
if hasattr(self.sent_requests[i], '_reply'): | |
req = self.sent_requests[i] | |
del self.sent_requests[:i + 1] | |
return req | |
# Reply for an unknown request? No, that can't happen. | |
else: | |
raise RuntimeError("Request reply to unknown request. Can't happen!") | |
def parse_connection_setup(self): | |
"""Internal function used to parse connection setup response. | |
""" | |
# Only the ConnectionSetupRequest has been sent so far | |
r = self.sent_requests[0] | |
while True: | |
# print 'data_send:', repr(self.data_send) | |
# print 'data_recv:', repr(self.data_recv) | |
if r._data: | |
alen = r._data['additional_length'] * 4 | |
# The full response haven't arrived yet | |
if len(self.data_recv) < alen: | |
return False | |
# Connection failed or further authentication is needed. | |
# Set reason to the reason string | |
if r._data['status'] != 1: | |
r._data['reason'] = self.data_recv[:r._data['reason_length']] | |
# Else connection succeeded, parse the reply | |
else: | |
x, d = r._success_reply.parse_binary(self.data_recv[:alen], | |
self, rawdict = True) | |
r._data.update(x) | |
del self.sent_requests[0] | |
self.data_recv = self.data_recv[alen:] | |
return True | |
else: | |
# The base reply is 8 bytes long | |
if len(self.data_recv) < 8: | |
return False | |
r._data, d = r._reply.parse_binary(self.data_recv[:8], | |
self, rawdict = True) | |
self.data_recv = self.data_recv[8:] | |
# Loop around to see if we have got the additional data | |
# already | |
PixmapFormat = rq.Struct( rq.Card8('depth'), | |
rq.Card8('bits_per_pixel'), | |
rq.Card8('scanline_pad'), | |
rq.Pad(5) | |
) | |
VisualType = rq.Struct ( rq.Card32('visual_id'), | |
rq.Card8('visual_class'), | |
rq.Card8('bits_per_rgb_value'), | |
rq.Card16('colormap_entries'), | |
rq.Card32('red_mask'), | |
rq.Card32('green_mask'), | |
rq.Card32('blue_mask'), | |
rq.Pad(4) | |
) | |
Depth = rq.Struct( rq.Card8('depth'), | |
rq.Pad(1), | |
rq.LengthOf('visuals', 2), | |
rq.Pad(4), | |
rq.List('visuals', VisualType) | |
) | |
Screen = rq.Struct( rq.Window('root'), | |
rq.Colormap('default_colormap'), | |
rq.Card32('white_pixel'), | |
rq.Card32('black_pixel'), | |
rq.Card32('current_input_mask'), | |
rq.Card16('width_in_pixels'), | |
rq.Card16('height_in_pixels'), | |
rq.Card16('width_in_mms'), | |
rq.Card16('height_in_mms'), | |
rq.Card16('min_installed_maps'), | |
rq.Card16('max_installed_maps'), | |
rq.Card32('root_visual'), | |
rq.Card8('backing_store'), | |
rq.Card8('save_unders'), | |
rq.Card8('root_depth'), | |
rq.LengthOf('allowed_depths', 1), | |
rq.List('allowed_depths', Depth) | |
) | |
class ConnectionSetupRequest(rq.GetAttrData): | |
_request = rq.Struct( rq.Set('byte_order', 1, (0x42, 0x6c)), | |
rq.Pad(1), | |
rq.Card16('protocol_major'), | |
rq.Card16('protocol_minor'), | |
rq.LengthOf('auth_prot_name', 2), | |
rq.LengthOf('auth_prot_data', 2), | |
rq.Pad(2), | |
rq.String8('auth_prot_name'), | |
rq.String8('auth_prot_data') ) | |
_reply = rq.Struct ( rq.Card8('status'), | |
rq.Card8('reason_length'), | |
rq.Card16('protocol_major'), | |
rq.Card16('protocol_minor'), | |
rq.Card16('additional_length') ) | |
_success_reply = rq.Struct( rq.Card32('release_number'), | |
rq.Card32('resource_id_base'), | |
rq.Card32('resource_id_mask'), | |
rq.Card32('motion_buffer_size'), | |
rq.LengthOf('vendor', 2), | |
rq.Card16('max_request_length'), | |
rq.LengthOf('roots', 1), | |
rq.LengthOf('pixmap_formats', 1), | |
rq.Card8('image_byte_order'), | |
rq.Card8('bitmap_format_bit_order'), | |
rq.Card8('bitmap_format_scanline_unit'), | |
rq.Card8('bitmap_format_scanline_pad'), | |
rq.Card8('min_keycode'), | |
rq.Card8('max_keycode'), | |
rq.Pad(4), | |
rq.String8('vendor'), | |
rq.List('pixmap_formats', PixmapFormat), | |
rq.List('roots', Screen), | |
) | |
def __init__(self, display, *args, **keys): | |
self._binary = self._request.to_binary(*args, **keys) | |
self._data = None | |
# Don't bother about locking, since no other threads have | |
# access to the display yet | |
display.request_queue.append((self, True)) | |
# However, we must lock send_and_recv, but we don't have | |
# to loop. | |
display.send_recv_lock.acquire() | |
display.send_and_recv(request = -1) | |