def main(): root.change_attributes(event_mask=X.PropertyChangeMask) is_bad_window = False while True: win, changed = get_active_window() if not is_bad_window or changed: try: # TODO: Application/window -specific detection configuration is_bad_window = False win.map() width = win.get_geometry().width height = 1 raw = win.get_image(0, 1, width, height, X.ZPixmap, 0xffffffff) image = Image.frombytes('RGB', (width, height), raw.data, 'raw', 'BGRX') colors = image.getcolors() if colors is None: target_color = (0, 0, 0) else: target_color = sorted(colors, key=lambda k: k[0])[-1][1] wm_class = ' '.join(win.get_wm_class()) name = '_'.join(win.get_wm_class()) set_window_config(wm_class, name, ','.join(map(str, target_color))) except Exception as e: print(e) is_bad_window = True while display.pending_events(): event = display.next_event() time.sleep(1 / 60)
def on_event(): nonlocal bound while display.pending_events(): evt = display.next_event() if evt.type == X.KeyPress: k = (evt.detail, evt.state & modmask) if k in bound: asyncio.ensure_future(bound[k]()) if evt.type == X.MappingNotify and evt.request == X.MappingKeyboard: display.refresh_keyboard_mapping(evt) regrab_keys()
def __update_focus_cb(self, command, sync=False): # Check FocusIn/FocusOut events if self.focus: focus_in = None focus_out = None for i in range(display.pending_events()): event = display.next_event() if event.type == Xlib.X.FocusIn: focus_in = event.window elif event.type == Xlib.X.FocusOut: focus_out = event.window if focus_in == self.focus: if focus_out != self.focus: # This is ugly workaround, but necessary to avoid a # problem that the language bar doesn't appear after # moving to other workspace in Ubuntu Unity desktop. # If old version of ibus.el which doesn't define # `ibus-redo-focus-in-cb' is running on Emacs, the # following message will just be ignored and take no effect. print_command('ibus_redo_focus_in_cb') if sync: print_command(command, focus_in.id) return True # Main part focus = self.display.get_input_focus().focus try: # get_input_focus() may return an integer 0 that query_tree() # causes AttributeError when X session is going to logout. tree = focus.query_tree() # In Ubuntu's Unity desktop, get_input_focus() often returns root # window incorrectly after changing workspace. if focus != tree.root: if not (focus.get_wm_class() or focus.get_wm_name()): focus = tree.parent if focus != self.focus or sync: print_command(command, focus.id) focus.change_attributes(event_mask=Xlib.X.FocusChangeMask) self.focus = focus return True except AttributeError: if sync: print_command(command, 0) return True # Fallback atom = display.get_atom("_NET_ACTIVE_WINDOW", True) focus_id = tree.root.get_property(atom, Xlib.Xatom.WINDOW, 0, 1).value[0] focus = display.create_resource_object("window", focus_id) if focus != self.focus or sync: print_command(command, focus_id) focus.change_attributes(event_mask=Xlib.X.FocusChangeMask) self.focus = focus return True
def do_input(): global old_keys new_keys = [] # Map cursor position to key presses if blob_pos is not None: if blob_pos[0] > 2.0 / 3.0 * WINDOW_WIDTH: new_keys.append('d') elif blob_pos[0] < 1.0 / 3.0 * WINDOW_WIDTH: new_keys.append('a') if blob_pos[1] > 2.0 / 3.0 * WINDOW_HEIGHT: new_keys.append('w') elif blob_pos[1] < 1.0 / 3.0 * WINDOW_HEIGHT: new_keys.append('s') if glfw.get_key(window, glfw.KEY_ENTER) == glfw.PRESS: new_keys.append('Escape') # Send press and release events when key state changes for char in ['w', 'a', 's', 'd', 'Escape']: new = char in new_keys old = char in old_keys if new: if not old: display.send_event(dest_window, keyev(char, True), event_mask=1) elif old: if not new: display.send_event(dest_window, keyev(char, False), event_mask=1) # Events don't get processed unless they are manually flushed while display.pending_events() > 0: display.poll_events() old_keys = new_keys
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import Xlib from Xlib import display, X # X is also needed display = Xlib.display.Display() screen = display.screen() root = screen.root #print(root.get_attributes()) root.change_attributes(event_mask=X.ExposureMask) # "adds" this event mask #print(root.get_attributes()) # see the difference gc = root.create_gc(foreground = screen.white_pixel, background = screen.black_pixel) def draw_it(): root.draw_text(gc, 100, 100, b"Hello, world!") display.flush() draw_it() while 1: if display.pending_events() != 0: # check to safely apply next_event event = display.next_event() if event.type == X.Expose and event.count == 0: draw_it()
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)
def on_event(): while display.pending_events(): display.next_event()