1
# Xlib.display -- high level display object
3
# Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se>
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
# Xlib.protocol modules
28
import protocol.display
29
from protocol import request, event, rq
31
# Xlib.xobjects modules
32
import xobject.resource
33
import xobject.drawable
34
import xobject.fontable
35
import xobject.colormap
38
_resource_baseclasses = {
39
'resource': xobject.resource.Resource,
40
'drawable': xobject.drawable.Drawable,
41
'window': xobject.drawable.Window,
42
'pixmap': xobject.drawable.Pixmap,
43
'fontable': xobject.fontable.Fontable,
44
'font': xobject.fontable.Font,
45
'gc': xobject.fontable.GC,
46
'colormap': xobject.colormap.Colormap,
47
'cursor': xobject.cursor.Cursor,
50
_resource_hierarchy = {
51
'resource': ('drawable', 'window', 'pixmap',
52
'fontable', 'font', 'gc',
53
'colormap', 'cursor'),
54
'drawable': ('window', 'pixmap'),
55
'fontable': ('font', 'gc')
58
class _BaseDisplay(protocol.display.Display):
59
resource_classes = _resource_baseclasses.copy()
61
# Implement a cache of atom names, used by Window objects when
62
# dealing with some ICCCM properties not defined in Xlib.Xatom
64
def __init__(self, *args, **keys):
65
apply(protocol.display.Display.__init__, (self, ) + args, keys)
68
def get_atom(self, atomname, only_if_exists=0):
69
if self._atom_cache.has_key(atomname):
70
return self._atom_cache[atomname]
72
r = request.InternAtom(display = self, name = atomname, only_if_exists = only_if_exists)
74
# don't cache NONE responses in case someone creates this later
76
self._atom_cache[atomname] = r.atom
82
def __init__(self, display = None):
83
self.display = _BaseDisplay(display)
85
# Create the keymap cache
86
self._keymap_codes = [()] * 256
87
self._keymap_syms = {}
88
self._update_keymap(self.display.info.min_keycode,
89
(self.display.info.max_keycode
90
- self.display.info.min_keycode + 1))
92
# Translations for keysyms to strings.
93
self.keysym_translations = {}
95
# Find all supported extensions
97
self.class_extension_dicts = {}
98
self.display_extension_methods = {}
100
self.extension_event = rq.DictWrapper({})
102
exts = self.list_extensions()
104
# Go through all extension modules
105
for extname, modname in ext.__extensions__:
108
# Import the module and fetch it
109
__import__('Xlib.ext.' + modname)
110
mod = getattr(ext, modname)
112
info = self.query_extension(extname)
113
self.display.set_extension_major(extname, info.major_opcode)
115
# Call initialiasation function
118
self.extensions.append(extname)
121
# Finalize extensions by creating new classes
122
for type, dict in self.class_extension_dicts.items():
123
origcls = self.display.resource_classes[type]
124
self.display.resource_classes[type] = new.classobj(origcls.__name__,
128
# Problem: we have already created some objects without the
129
# extensions: the screen roots and default colormaps.
130
# Fix that by reinstantiating them.
131
for screen in self.display.info.roots:
132
screen.root = self.display.resource_classes['window'](self.display, screen.root.id)
133
screen.default_colormap = self.display.resource_classes['colormap'](self.display, screen.default_colormap.id)
136
def get_display_name(self):
137
"""Returns the name used to connect to the server, either
138
provided when creating the Display object, or fetched from the
139
environmental variable $DISPLAY."""
140
return self.display.get_display_name()
143
"""Returns the file descriptor number of the underlying socket.
144
This method is provided to allow Display objects to be passed
146
return self.display.fileno()
149
"""Close the display, freeing the resources that it holds."""
152
def set_error_handler(self, handler):
153
"""Set the default error handler which will be called for all
154
unhandled errors. handler should take two arguments as a normal
155
request error handler, but the second argument (the request) will
156
be None. See section Error Handling."""
157
self.display.set_error_handler(handler)
160
"""Flush the request queue, building and sending the queued
161
requests. This can be necessary in applications that never wait
162
for events, and in threaded applications."""
166
"""Flush the queue and wait until the server has processed all
167
the queued requests. Use this e.g. when it is important that
168
errors caused by a certain request is trapped."""
169
# Do a light-weight replyrequest to sync. There must
170
# be a better way to do it...
171
self.get_pointer_control()
173
def next_event(self):
174
"""Return the next event. If there are no events queued, it will
175
block until the next event is fetched from the server."""
176
return self.display.next_event()
178
def pending_events(self):
179
"""Return the number of events queued, i.e. the number of times
180
that Display.next_event() can be called without blocking."""
181
return self.display.pending_events()
183
def has_extension(self, extension):
184
"""Check if both the server and the client library support the X
185
extension named extension."""
186
return extension in self.extensions
188
def create_resource_object(self, type, id):
189
"""Create a resource object of type for the integer id. type
190
should be one of the following strings:
202
This function can be used when a resource ID has been fetched
203
e.g. from an resource or a command line argument. Resource
204
objects should never be created by instantiating the appropriate
205
class directly, since any X extensions dynamically added by the
206
library will not be available.
208
return self.display.resource_classes[type](self.display, id)
210
# We need this to handle display extension methods
211
def __getattr__(self, attr):
213
function = self.display_extension_methods[attr]
214
return new.instancemethod(function, self, self.__class__)
216
raise AttributeError(attr)
219
### display information retrieval
222
def screen(self, sno = None):
224
return self.display.info.roots[self.display.default_screen]
226
return self.display.info.roots[sno]
228
def screen_count(self):
229
"""Return the total number of screens on the display."""
230
return len(self.display.info.roots)
232
def get_default_screen(self):
233
"""Return the number of the default screen, extracted from the
235
return self.display.get_default_screen()
238
### Extension module interface
241
def extension_add_method(self, object, name, function):
242
"""extension_add_method(object, name, function)
244
Add an X extension module method. OBJECT is the type of
245
object to add the function to, a string from this list:
258
NAME is the name of the method, a string. FUNCTION is a
259
normal function whose first argument is a 'self'.
262
if object == 'display':
263
if hasattr(self, name):
264
raise error.MethodOverrideError('attempting to replace display method: %s' % name)
266
self.display_extension_methods[name] = function
269
types = (object, ) + _resource_hierarchy.get(object, ())
271
cls = _resource_baseclasses[type]
272
if hasattr(cls, name):
273
raise error.MethodOverrideError('attempting to replace %s method: %s' % (type, name))
275
method = new.instancemethod(function, None, cls)
277
# Maybe should check extension overrides too
279
self.class_extension_dicts[type][name] = method
281
self.class_extension_dicts[type] = { name: method }
283
def extension_add_event(self, code, evt, name = None):
284
"""extension_add_event(code, evt, [name])
286
Add an extension event. CODE is the numeric code, and EVT is
287
the event class. EVT will be cloned, and the attribute _code
288
of the new event class will be set to CODE.
290
If NAME is ommitted, it will be set to the name of EVT. This
291
name is used to insert an entry in the DictWrapper
295
newevt = new.classobj(evt.__name__, evt.__bases__,
299
self.display.add_extension_event(code, newevt)
304
setattr(self.extension_event, name, code)
307
def add_extension_error(self, code, err):
308
"""add_extension_error(code, err)
310
Add an extension error. CODE is the numeric code, and ERR is
314
self.display.add_extension_error(code, err)
317
### keymap cache implementation
320
# The keycode->keysym map is stored in a list with 256 elements.
321
# Each element represents a keycode, and the tuple elements are
322
# the keysyms bound to the key.
324
# The keysym->keycode map is stored in a mapping, where the keys
325
# are keysyms. The values are a sorted list of tuples with two
326
# elements each: (index, keycode)
327
# keycode is the code for a key to which this keysym is bound, and
328
# index is the keysyms index in the map for that keycode.
330
def keycode_to_keysym(self, keycode, index):
331
"""Convert a keycode to a keysym, looking in entry index.
332
Normally index 0 is unshifted, 1 is shifted, 2 is alt grid, and 3
333
is shift+alt grid. If that key entry is not bound, X.NoSymbol is
336
return self._keymap_codes[keycode][index]
340
def keysym_to_keycode(self, keysym):
341
"""Look up the primary keycode that is bound to keysym. If
342
several keycodes are found, the one with the lowest index and
343
lowest code is returned. If keysym is not bound to any key, 0 is
346
return self._keymap_syms[keysym][0][1]
347
except (KeyError, IndexError):
350
def keysym_to_keycodes(self, keysym):
351
"""Look up all the keycodes that is bound to keysym. A list of
352
tuples (keycode, index) is returned, sorted primarily on the
353
lowest index and secondarily on the lowest keycode."""
355
# Copy the map list, reversing the arguments
356
return map(lambda x: (x[1], x[0]), self._keymap_syms[keysym])
360
def refresh_keyboard_mapping(self, evt):
361
"""This method should be called once when a MappingNotify event
362
is received, to update the keymap cache. evt should be the event
364
if isinstance(evt, event.MappingNotify):
365
if evt.request == X.MappingKeyboard:
366
self._update_keymap(evt.first_keycode, evt.count)
368
raise TypeError('expected a MappingNotify event')
370
def _update_keymap(self, first_keycode, count):
371
"""Internal function, called to refresh the keymap cache.
374
# Delete all sym->code maps for the changed codes
376
lastcode = first_keycode + count
377
for keysym, codes in self._keymap_syms.items():
379
while i < len(codes):
381
if code >= first_keycode and code < lastcode:
386
# Get the new keyboard mapping
387
keysyms = self.get_keyboard_mapping(first_keycode, count)
389
# Replace code->sym map with the new map
390
self._keymap_codes[first_keycode:lastcode] = keysyms
392
# Update sym->code map
397
if sym != X.NoSymbol:
398
if self._keymap_syms.has_key(sym):
399
symcodes = self._keymap_syms[sym]
400
symcodes.append((index, code))
403
self._keymap_syms[sym] = [(index, code)]
409
### client-internal keysym to string translations
412
def lookup_string(self, keysym):
413
"""Return a string corresponding to KEYSYM, or None if no
414
reasonable translation is found.
416
s = self.keysym_translations.get(keysym)
421
return Xlib.XK.keysym_to_string(keysym)
423
def rebind_string(self, keysym, newstring):
424
"""Change the translation of KEYSYM to NEWSTRING.
425
If NEWSTRING is None, remove old translation if any.
427
if newstring is None:
429
del self.keysym_translations[keysym]
433
self.keysym_translations[keysym] = newstring
440
def intern_atom(self, name, only_if_exists = 0):
441
"""Intern the string name, returning its atom number. If
442
only_if_exists is true and the atom does not already exist, it
443
will not be created and X.NONE is returned."""
444
r = request.InternAtom(display = self.display,
446
only_if_exists = only_if_exists)
449
def get_atom(self, atom, only_if_exists = 0):
450
"""Alias for intern_atom, using internal cache"""
451
return self.display.get_atom(atom, only_if_exists)
454
def get_atom_name(self, atom):
455
"""Look up the name of atom, returning it as a string. Will raise
456
BadAtom if atom does not exist."""
457
r = request.GetAtomName(display = self.display,
461
def get_selection_owner(self, selection):
462
"""Return the window that owns selection (an atom), or X.NONE if
463
there is no owner for the selection. Can raise BadAtom."""
464
r = request.GetSelectionOwner(display = self.display,
465
selection = selection)
468
def send_event(self, destination, event, event_mask = 0, propagate = 0,
470
"""Send a synthetic event to the window destination which can be
471
a window object, or X.PointerWindow or X.InputFocus. event is the
472
event object to send, instantiated from one of the classes in
473
protocol.events. See XSendEvent(3X11) for details.
475
There is also a Window.send_event() method."""
476
request.SendEvent(display = self.display,
478
propagate = propagate,
479
destination = destination,
480
event_mask = event_mask,
483
def ungrab_pointer(self, time, onerror = None):
484
"""elease a grabbed pointer and any queued events. See
485
XUngrabPointer(3X11)."""
486
request.UngrabPointer(display = self.display,
490
def change_active_pointer_grab(self, event_mask, cursor, time, onerror = None):
491
"""Change the dynamic parameters of a pointer grab. See
492
XChangeActivePointerGrab(3X11)."""
493
request.ChangeActivePointerGrab(display = self.display,
497
event_mask = event_mask)
499
def ungrab_keyboard(self, time, onerror = None):
500
"""Ungrab a grabbed keyboard and any queued events. See
501
XUngrabKeyboard(3X11)."""
502
request.UngrabKeyboard(display = self.display,
506
def allow_events(self, mode, time, onerror = None):
507
"""Release some queued events. mode should be one of
508
X.AsyncPointer, X.SyncPointer, X.AsyncKeyboard, X.SyncKeyboard,
509
X.ReplayPointer, X.ReplayKeyboard, X.AsyncBoth, or X.SyncBoth.
510
time should be a timestamp or X.CurrentTime."""
511
request.AllowEvents(display = self.display,
516
def grab_server(self, onerror = None):
517
"""Disable processing of requests on all other client connections
518
until the server is ungrabbed. Server grabbing should be avoided
519
as much as possible."""
520
request.GrabServer(display = self.display,
523
def ungrab_server(self, onerror = None):
524
"""Release the server if it was previously grabbed by this client."""
525
request.UngrabServer(display = self.display,
528
def warp_pointer(self, x, y, src_window = X.NONE, src_x = 0, src_y = 0,
529
src_width = 0, src_height = 0, onerror = None):
530
"""Move the pointer relative its current position by the offsets
531
(x, y). However, if src_window is a window the pointer is only
532
moved if the specified rectangle in src_window contains it. If
533
src_width is 0 it will be replaced with the width of src_window -
534
src_x. src_height is treated in a similar way.
536
To move the pointer to absolute coordinates, use Window.warp_pointer()."""
537
request.WarpPointer(display = self.display,
539
src_window = src_window,
543
src_width = src_width,
544
src_height = src_height,
548
def set_input_focus(self, focus, revert_to, time, onerror = None):
549
"""Set input focus to focus, which should be a window,
550
X.PointerRoot or X.NONE. revert_to specifies where the focus
551
reverts to if the focused window becomes not visible, and should
552
be X.RevertToParent, RevertToPointerRoot, or RevertToNone. See
553
XSetInputFocus(3X11) for details.
555
There is also a Window.set_input_focus()."""
556
request.SetInputFocus(display = self.display,
558
revert_to = revert_to,
562
def get_input_focus(self):
563
"""Return an object with the following attributes:
566
The window which currently holds the input
567
focus, X.NONE or X.PointerRoot.
569
Where the focus will revert, one of X.RevertToParent,
570
RevertToPointerRoot, or RevertToNone. """
571
return request.GetInputFocus(display = self.display)
573
def query_keymap(self):
574
"""Return a bit vector for the logical state of the keyboard,
575
where each bit set to 1 indicates that the corresponding key is
576
currently pressed down. The vector is represented as a list of 32
577
integers. List item N contains the bits for keys 8N to 8N + 7
578
with the least significant bit in the byte representing key 8N."""
579
r = request.QueryKeymap(display = self.display)
582
def open_font(self, name):
583
"""Open the font identifed by the pattern name and return its
584
font object. If name does not match any font, None is returned."""
585
fid = self.display.allocate_resource_id()
586
ec = error.CatchError(error.BadName)
588
request.OpenFont(display = self.display,
595
self.display.free_resource_id(fid)
598
cls = self.display.get_resource_class('font', xobject.fontable.Font)
599
return cls(self.display, fid, owner = 1)
601
def list_fonts(self, pattern, max_names):
602
"""Return a list of font names matching pattern. No more than
603
max_names will be returned."""
604
r = request.ListFonts(display = self.display,
605
max_names = max_names,
609
def list_fonts_with_info(self, pattern, max_names):
610
"""Return a list of fonts matching pattern. No more than
611
max_names will be returned. Each list item represents one font
612
and has the following properties:
615
The name of the font.
628
See the descripton of XFontStruct in XGetFontProperty(3X11)
629
for details on these values.
631
A list of properties. Each entry has two attributes:
634
The atom identifying this property.
636
A 32-bit unsigned value.
638
return request.ListFontsWithInfo(display = self.display,
639
max_names = max_names,
642
def set_font_path(self, path, onerror = None):
643
"""Set the font path to path, which should be a list of strings.
644
If path is empty, the default font path of the server will be
646
request.SetFontPath(display = self.display,
650
def get_font_path(self):
651
"""Return the current font path as a list of strings."""
652
r = request.GetFontPath(display = self.display)
655
def query_extension(self, name):
656
"""Ask the server if it supports the extension name. If it is
657
supported an object with the following attributes is returned:
660
The major opcode that the requests of this extension uses.
662
The base event code if the extension have additional events, or 0.
664
The base error code if the extension have additional errors, or 0.
666
If the extension is not supported, None is returned."""
667
r = request.QueryExtension(display = self.display,
674
def list_extensions(self):
675
"""Return a list of all the extensions provided by the server."""
676
r = request.ListExtensions(display = self.display)
679
def change_keyboard_mapping(self, first_keycode, keysyms, onerror = None):
680
"""Modify the keyboard mapping, starting with first_keycode.
681
keysyms is a list of tuples of keysyms. keysyms[n][i] will be
682
assigned to keycode first_keycode+n at index i."""
683
request.ChangeKeyboardMapping(display = self.display,
685
first_keycode = first_keycode,
688
def get_keyboard_mapping(self, first_keycode, count):
689
"""Return the current keyboard mapping as a list of tuples,
690
starting at first_keycount and no more than count."""
691
r = request.GetKeyboardMapping(display = self.display,
692
first_keycode = first_keycode,
696
def change_keyboard_control(self, onerror = None, **keys):
697
"""Change the parameters provided as keyword arguments:
700
The volume of key clicks between 0 (off) and 100 (load).
701
-1 will restore default setting.
703
The base volume of the bell, coded as above.
705
The pitch of the bell in Hz, -1 restores the default.
707
The duration of the bell in milliseconds, -1 restores
712
led_mode should be X.LedModeOff or X.LedModeOn. If led is
713
provided, it should be a 32-bit mask listing the LEDs that
714
should change. If led is not provided, all LEDs are changed.
718
auto_repeat_mode should be one of X.AutoRepeatModeOff,
719
X.AutoRepeatModeOn, or X.AutoRepeatModeDefault. If key is
720
provided, that key will be modified, otherwise the global
721
state for the entire keyboard will be modified."""
722
request.ChangeKeyboardControl(display = self.display,
726
def get_keyboard_control(self):
727
"""Return an object with the following attributes:
730
X.AutoRepeatModeOn or X.AutoRepeatModeOff.
733
A list of 32 integers. List item N contains the bits for keys
734
8N to 8N + 7 with the least significant bit in the byte
735
representing key 8N. If a bit is on, autorepeat is enabled
736
for the corresponding key.
739
A 32-bit mask indicating which LEDs are on.
742
The volume of key click, from 0 to 100.
749
The volume, pitch and duration of the bell. """
750
return request.GetKeyboardControl(display = self.display)
752
def bell(self, percent = 0, onerror = None):
753
"""Ring the bell at the volume percent which is relative the base
754
volume. See XBell(3X11)."""
755
request.Bell(display = self.display,
759
def change_pointer_control(self, accel = None, threshold = None, onerror = None):
760
"""To change the pointer acceleration, set accel to a tuple (num,
761
denum). The pointer will then move num/denum times the normal
762
speed if it moves beyond the threshold number of pixels at once.
763
To change the threshold, set it to the number of pixels. -1
764
restores the default."""
771
accel_num, accel_denum = accel
773
if threshold is None:
778
request.ChangePointerControl(display = self.display,
781
do_thres = do_threshold,
782
accel_num = accel_num,
783
accel_denum = accel_denum,
784
threshold = threshold)
786
def get_pointer_control(self):
787
"""Return an object with the following attributes:
792
The acceleration as numerator/denumerator.
795
The number of pixels the pointer must move before the
796
acceleration kicks in."""
797
return request.GetPointerControl(display = self.display)
799
def set_screen_saver(self, timeout, interval, prefer_blank, allow_exposures, onerror = None):
800
"""See XSetScreenSaver(3X11)."""
801
request.SetScreenSaver(display = self.display,
805
prefer_blank = prefer_blank,
806
allow_exposures = allow_exposures)
808
def get_screen_saver(self):
809
"""Return an object with the attributes timeout, interval,
810
prefer_blanking, allow_exposures. See XGetScreenSaver(3X11) for
812
return request.GetScreenSaver(display = self.display)
814
def change_hosts(self, mode, host_family, host, onerror = None):
815
"""mode is either X.HostInsert or X.HostDelete. host_family is
816
one of X.FamilyInternet, X.FamilyDECnet or X.FamilyChaos.
818
host is a list of bytes. For the Internet family, it should be the
819
four bytes of an IPv4 address."""
820
request.ChangeHosts(display = self.display,
823
host_family = host_family,
826
def list_hosts(self):
827
"""Return an object with the following attributes:
830
X.EnableAccess if the access control list is used, X.DisableAccess otherwise.
832
The hosts on the access list. Each entry has the following attributes:
835
X.FamilyInternet, X.FamilyDECnet, or X.FamilyChaos.
837
A list of byte values, the coding depends on family. For the Internet family, it is the 4 bytes of an IPv4 address.
840
return request.ListHosts(display = self.display)
842
def set_access_control(self, mode, onerror = None):
843
"""Enable use of access control lists at connection setup if mode
844
is X.EnableAccess, disable if it is X.DisableAccess."""
845
request.SetAccessControl(display = self.display,
849
def set_close_down_mode(self, mode, onerror = None):
850
"""Control what will happen with the client's resources at
851
connection close. The default is X.DestroyAll, the other values
852
are X.RetainPermanent and X.RetainTemporary."""
853
request.SetCloseDownMode(display = self.display,
857
def force_screen_saver(self, mode, onerror = None):
858
"""If mode is X.ScreenSaverActive the screen saver is activated.
859
If it is X.ScreenSaverReset, the screen saver is deactivated as
860
if device input had been received."""
861
request.ForceScreenSaver(display = self.display,
865
def set_pointer_mapping(self, map):
866
"""Set the mapping of the pointer buttons. map is a list of
867
logical button numbers. map must be of the same length as the
868
list returned by Display.get_pointer_mapping().
871
logical number for the physical button n+1. Logical number 0
872
disables the button. Two physical buttons cannot be mapped to the
875
If one of the buttons to be altered are
876
logically in the down state, X.MappingBusy is returned and the
877
mapping is not changed. Otherwise the mapping is changed and
878
X.MappingSuccess is returned."""
879
r = request.SetPointerMapping(display = self.display,
883
def get_pointer_mapping(self):
884
"""Return a list of the pointer button mappings. Entry N in the
885
list sets the logical button number for the physical button N+1."""
886
r = request.GetPointerMapping(display = self.display)
889
def set_modifier_mapping(self, keycodes):
890
"""Set the keycodes for the eight modifiers X.Shift, X.Lock,
891
X.Control, X.Mod1, X.Mod2, X.Mod3, X.Mod4 and X.Mod5. keycodes
892
should be a eight-element list where each entry is a list of the
893
keycodes that should be bound to that modifier.
896
key is logically in the down state, X.MappingBusy is returned and
897
the mapping is not changed. If the mapping violates some server
898
restriction, X.MappingFailed is returned. Otherwise the mapping
899
is changed and X.MappingSuccess is returned."""
900
r = request.SetModifierMapping(display = self.display,
904
def get_modifier_mapping(self):
905
"""Return a list of eight lists, one for each modifier. The list
906
can be indexed using X.ShiftMapIndex, X.Mod1MapIndex, and so on.
907
The sublists list the keycodes bound to that modifier."""
908
r = request.GetModifierMapping(display = self.display)
911
def no_operation(self, onerror = None):
912
"""Do nothing but send a request to the server."""
913
request.NoOperation(display = self.display,