1076 lines
36 KiB
Python
1076 lines
36 KiB
Python
# -*- 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)
|