def send_event(client_id, keystate, shiftstate, keycode): display = Xlib.display.Display() Xlib_root_window = display.screen().root window = display.create_resource_object('window', client_id) if keystate == 2: XEVENT = Xlib.protocol.event.KeyPress elif keystate == 3: XEVENT = Xlib.protocol.event.KeyRelease else: raise event = XEVENT(time=int(time.time()), root=Xlib_root_window, window=window, same_screen=0, child=Xlib.X.NONE, root_x=0, root_y=0, event_x=0, event_y=0, state=shiftstate, detail=keycode) display.sync() p = window.send_event(event, propagate=False) display.sync() display.close() return True
def close_x(): ''' Closes the connection to X, but flushes it first ''' global display display.flush() display.close()
def main() -> None: for cmd in CMDS: assert shutil.which(cmd) is not None, "'%s' not found in PATH." % cmd signal.signal(signal.SIGCHLD, signal.SIG_IGN) display: Xlib.display.Display = Xlib.display.Display() window_manager: WindowManager = WindowManager(display) while window_manager.is_running(): window_manager.on_event(display.next_event()) display.close()
def remap_keys(remap): """ Remap keycode -> keysym for function keys The keycode -> keysym mapping is so that the keycode generated when the F1 key is pressed sends the F25 keysym. But to do that you need to know the keycodes for F1-10, for Linux on x86 the keycode for F1 is 67 but for other platforms it could be different. So the code looks up the keycode mapped to F1 keysym if that is non zero (i.e. F1 is mapped) I map that keycode to F1 or F25 as required, if F1 is not mapped I lookup the keycode for F25 and if that is non zero I map that keycode to F1 or F25 as required. If F1 and F25 are not mapped to any keycode I use 67 as the keycode for the F1 key. The code assumes that the keycodes for F1-10 are sequential which may not be true for all platforms. A more robust solution may be found by accessing the keyboard layout somehow but I haven't had time to look yet. """ display = Xlib.display.Display() # only keycodes 8 to 255 are defined first_keycode = 8 keymaps_orig = display.get_keyboard_mapping(first_keycode, 255 - first_keycode) keymaps = list(keymaps_orig) if remap == True: keysym = 65494 else: keysym = 65470 keycode_F1 = display.keysym_to_keycode(65470) keycode_F25 = display.keysym_to_keycode(65494) # set first_fkeycode to a sensible default incase F1 and F25 are not mapped first_fkeycode = 67 if keycode_F1 > 0: first_fkeycode = keycode_F1 elif keycode_F25 > 0: first_fkeycode = keycode_F25 for i in range(0, 10): keymaps[(i + first_fkeycode - first_keycode)][0] = keysym + i keymaps[(i + first_fkeycode - first_keycode)][2] = keysym + i keymaps[(i + first_fkeycode - first_keycode)][4] = keysym + i display.change_keyboard_mapping(first_keycode, keymaps, onerror = None) display.sync() display.close()
def remap_keys(remap): """ Remap keycode -> keysym for function keys The keycode -> keysym mapping is so that the keycode generated when the F1 key is pressed sends the F25 keysym. But to do that you need to know the keycodes for F1-10, for Linux on x86 the keycode for F1 is 67 but for other platforms it could be different. So the code looks up the keycode mapped to F1 keysym if that is non zero (i.e. F1 is mapped) I map that keycode to F1 or F25 as required, if F1 is not mapped I lookup the keycode for F25 and if that is non zero I map that keycode to F1 or F25 as required. If F1 and F25 are not mapped to any keycode I use 67 as the keycode for the F1 key. The code assumes that the keycodes for F1-10 are sequential which may not be true for all platforms. A more robust solution may be found by accessing the keyboard layout somehow but I haven't had time to look yet. """ display = Xlib.display.Display() # only keycodes 8 to 255 are defined first_keycode = 8 keymaps_orig = display.get_keyboard_mapping(first_keycode, 255 - first_keycode) keymaps = list(keymaps_orig) if remap == True: keysym = 65494 else: keysym = 65470 keycode_F1 = display.keysym_to_keycode(65470) keycode_F25 = display.keysym_to_keycode(65494) # set first_fkeycode to a sensible default incase F1 and F25 are not mapped first_fkeycode = 67 if keycode_F1 > 0: first_fkeycode = keycode_F1 elif keycode_F25 > 0: first_fkeycode = keycode_F25 for i in range(0, int(number_of_suggestions)): keymaps[(i + first_fkeycode - first_keycode)][0] = keysym + i keymaps[(i + first_fkeycode - first_keycode)][2] = keysym + i keymaps[(i + first_fkeycode - first_keycode)][4] = keysym + i display.change_keyboard_mapping(first_keycode, keymaps, onerror = None) display.sync() display.close()
def setup(ui_model): keymap_mgr = ui_model.get_keymap_manager() scancode_rows = _get_scancode_rows() # Scancodes scancode_locs = {} for row, codes in enumerate(scancode_rows): for index, code in enumerate(codes): scancode_locs[code] = (row, index) keymap_mgr.set_scancode_locations(scancode_locs) if _has_xlib: display = Xlib.display.Display() key_names = {} for row, codes in enumerate(scancode_rows): for index, code in enumerate(codes): keysym = display.keycode_to_keysym(code, 0) if keysym != Xlib.X.NoSymbol: name = display.lookup_string(keysym) if name: key_names[(row, index)] = name.upper() display.close() keymap_mgr.set_key_names(key_names) # TODO: set key ID locations for systems that need them # Actions key_actions = { (0, 0): KeyboardAction(KeyboardAction.REST), (2, 9): KeyboardAction(KeyboardAction.OCTAVE_DOWN), (2, 10): KeyboardAction(KeyboardAction.OCTAVE_UP), (3, 7): KeyboardAction(KeyboardAction.PLAY), (3, 8): KeyboardAction(KeyboardAction.SILENCE), } tw_sizes = keymap_mgr.get_typewriter_row_sizes() tw_offsets = keymap_mgr.get_typewriter_row_offsets() for row, (size, offset) in enumerate(zip(tw_sizes, tw_offsets)): for index in range(size): loc = (row, index + offset) assert loc not in key_actions key_actions[loc] = KeyboardNoteAction(row, index) keymap_mgr.set_key_actions(key_actions)
def current_app(x): """ Metric function for MIDAS that returns the app currently in focus """ try: display = Xlib.display.Display() window = display.get_input_focus().focus wmclass = window.get_wm_class() if wmclass is None: window = window.query_tree().parent wmclass = window.get_wm_class() display.close() del display if wmclass: return(wmclass[1]) else: return('UNKNOWN') except: return('ERROR!')
def current_app(x): """ Metric function for MIDAS that returns the app currently in focus """ try: display = Xlib.display.Display() window = display.get_input_focus().focus wmclass = window.get_wm_class() if wmclass is None: window = window.query_tree().parent wmclass = window.get_wm_class() display.close() del display if wmclass: return (wmclass[1]) else: return ('UNKNOWN') except: return ('ERROR!')
import sys import threading import types import PIL import Xlib.display import Xlib.threaded import Xlib.XK from six.moves import queue from . import _base # Create a display to verify that we have an X connection display = Xlib.display.Display() display.close() del display class XError(Exception): """An error that is thrown at the end of a code block managed by a :func:`display_manager` if an *X* error occurred. """ pass @contextlib.contextmanager def display_manager(display): """Traps *X* errors and raises an :class:`XError` at the end if any error occurred.
def _check(): display = Xlib.display.Display() display.close()
def _check_and_initialize(): display = Xlib.display.Display() display.close() for group in Xlib.keysymdef.__all__: Xlib.XK.load_keysym_group(group)
def sendViaClipboard(blobs, record = None, txtselections = defSelections, ui=ui_null()): """ Send a list of blobs via the clipboard (using X selections, cut buffers are not yet supported) in sequence. Typically the PRIMARY and/or SECONDARY selections are used for middle click and shift+insert pasting, while the CLIPBOARD selection is often used by Ctrl+V pasting. Raises an XFailConnection exception if connecting to the X DISPLAY failed. """ global Xlib, X, Xatom, Xerror if Xlib is None: ui.status('Initialising python-Xlib, stand by...') ui.mainloop.draw_screen() try: from Xlib import X, Xatom, error as Xerror import Xlib.display except ImportError: ui.status('Error importing python-Xlib, X clipboard integration unavailable') return # Can't do this in the function definition - it seems that a .remove() # affects the default. That would be expected if it were assigned as a # reference to the default list, but even happens when the [:] syntax is # used to copy the list. I guess that must be an unexpected side effect of # the function definition only excuting once when the file is loaded? txtselections = txtselections[:] selections = txtselections[:] def findClientWindow(window, ui): """ walk up the tree looking for a client window """ def get_wm_client_leader(window): d = window.get_full_property(Xatom.WM_CLIENT_LEADER, Xatom.WINDOW) if d is None or d.format != 32 or len(d.value) < 1: return None else: cls = window.display.get_resource_class('window', type(window)) return cls(window.display, d.value[0]) host = window.get_wm_client_machine() while True: comm = window.get_full_property(Xatom.WM_COMMAND, Xatom.STRING) name = window.get_wm_name() # Nokia N900 uses _NET_WM_NAME instead of WM_NAME: netname = window.get_full_property(Xatom._NET_WM_NAME, Xatom.UTF8_STRING) leadercomm = None # Only one top-level window for a given client has WM_COMMAND. Find the # leader top-level window (if we are looking at a top-level window) and # check it's WM_COMMAND property. # # I don't see a requirement in the ICCCM that the client leader # necessarily be the window with the WM_COMMAND property, it may end up # being necessary to iterate all the windows with the same # WM_CLIENT_LEADER to find the one window that has the WM_COMMAND. leader = get_wm_client_leader(window) if leader is not None and leader != window: leadercomm = leader.get_full_property(Xatom.WM_COMMAND, Xatom.STRING) requestor = name or netname or comm or leadercomm if hasattr(requestor, 'value'): requestor = requestor.value if requestor: break resp = window.query_tree() root = resp.root; parent = resp.parent if parent == root: return ('<unknown>', host) window = parent return (requestor, host) def handleSelectionRequest(e, field, record, ui): global _prev_requestor if ((e.time != X.CurrentTime and e.time < timestamp) or # Timestamp out of bounds (e.selection not in selections) or # Requesting a different selection (e.owner.id != win.id)): # We aren't the owner return _refuseSelectionRequest(e) if (e.target in (Xatom.STRING, Xatom.TEXT)): (requestor, host) = findClientWindow(e.requestor, ui) if requestor.lower() in blacklist or any([ pattern.match(requestor) for pattern in blacklist_re ]): if requestor != _prev_requestor: ui.status("Ignoring request from %s@%s"%(requestor, host), append=True) _prev_requestor = requestor return _refuseSelectionRequest(e) ui.status("Sent %s for '%s' via %s to %s@%s"%(field.upper(), record, display.get_atom_name(e.selection), requestor, host), append=True) oldmask = e.requestor.get_attributes().your_event_mask e.requestor.change_attributes(event_mask = oldmask | X.PropertyChangeMask) _sendSelection(blob, Xatom.STRING, 8, e, ui) return True elif (e.target == Xatom.TIMESTAMP): _sendSelection([timestamp], Xatom.TIMESTAMP, 32, e, ui) elif (e.target == Xatom.TARGETS): _sendSelection([Xatom.TARGETS, Xatom.TIMESTAMP, Xatom.TEXT, Xatom.STRING], Xatom.ATOM, 32, e, ui) else: return _refuseSelectionRequest(e) return False # Opening the display prints 'Xlib.protocol.request.QueryExtension' to # stdout, so temporarily redirect it: ui.status('Connecting to display, stand by...') ui.mainloop.draw_screen() import sys, StringIO saved_stdout = sys.stdout sys.stdout = StringIO.StringIO() try: display = Xlib.display.Display() except Xerror.DisplayError: raise XFailConnection() finally: sys.stdout = saved_stdout screen = display.screen() win = screen.root.create_window(0,0,1,1,0,0) Xatom.TEXT = display.intern_atom('TEXT', True) Xatom.TARGETS = display.intern_atom('TARGETS', True) Xatom.TIMESTAMP = display.intern_atom('TIMESTAMP', True) Xatom.WM_CLIENT_LEADER = display.intern_atom('WM_CLIENT_LEADER', True) Xatom._NET_WM_NAME = display.intern_atom('_NET_WM_NAME', True) Xatom.UTF8_STRING = display.intern_atom('UTF8_STRING', True) ui_fds = ui.mainloop.screen.get_input_descriptors() if ui_fds is None: ui_fds = [] select_fds = set([display] + ui_fds) try: old = ui_tty.set_cbreak() # Set unbuffered IO (if not already) ui.status('') for (field, blob) in blobs: ui.status("Ready to send %s for '%s' via %s... (enter skips, escape cancels)"%(field.upper(),record,str(txtselections)), append=True) ui.mainloop.draw_screen() awaitingCompletion = [] timestamp = _ownSelections(display, win, selections) timeout = None skip = False while 1: if skip and awaitingCompletion == []: break while True: try: (readable, ign, ign) = select.select(select_fds, [], [], timeout) except select.error as e: if e.args[0] == 4: continue # Interrupted system call raise break if not readable and awaitingCompletion == []: break for fd in readable: if fd == sys.stdin.fileno(): char = sys.stdin.read(1) if char == '\n': skip = True elif char == '\x1b': return elif fd in ui_fds: # This is a hack to redraw the screen - we really should # restructure all this so as not to block instead: ui.mainloop.event_loop._loop() if fd == display: while display.pending_events(): e = display.next_event() if e.type == X.SelectionRequest: if handleSelectionRequest(e, field, record, ui): # Don't break immediately, transfer will not have finished. # Wait until the property has been deleted by the requestor awaitingCompletion.append((e.requestor, e.property)) elif e.type == X.PropertyNotify: if (e.window, e.atom) in awaitingCompletion \ and e.state == 1: # Deleted awaitingCompletion.remove((e.window, e.atom)) # Some programs, such as firefox (when pasting with # shift+insert), don't expect the selection to change suddenly # and behave badly if it does, so wait a moment before ending: timeout = 0.01 elif e.type == X.SelectionClear: if e.time == X.CurrentTime or e.time >= timestamp: # If we lost CLIPBOARD (explicit copy) or no longer control any selection, abort: name = display.get_atom_name(e.atom) selections.remove(e.atom) txtselections.remove(name) if name == 'CLIPBOARD' or not selections: # If transfer is in progress it should be allowed to complete: if awaitingCompletion == []: return timeout = 0.01 else: ui.status("Lost control of %s, still ready to send %s via %s..."%(name, field.upper() ,str(txtselections)), append=True) ui.mainloop.draw_screen() ui.mainloop.draw_screen() finally: ui_tty.restore_cbreak(old) win.destroy() display.close() # I may have introduced a bug while adding the urwid loop # stuff here - the clipboard selection remained grabbed # after destroying the window. This worked around it since # I can't see what is wrong. ui.status('Clipboard Cleared', append=True)
# You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import contextlib import itertools import Xlib.display import Xlib.threaded import Xlib.XK from . import AbstractListener from .xorg_keysyms import * # Create a display to verify that we have an X connection display = Xlib.display.Display() display.close() del display class X11Error(Exception): """An error that is thrown at the end of a code block managed by a :func:`display_manager` if an *X11* error occurred. """ pass @contextlib.contextmanager def display_manager(display): """Traps *X* errors and raises an :class:``X11Error`` at the end if any error occurred.