def _keyevent(self, event): # Store key press time (approximate to the current time # as the X event.time isn't synced with that) self.last_key_time = time.time() wmanager.debug('keys', '%s %d %d, keyhandler %s', event.__class__.__name__, event.detail, event.state, self) # Add in our release "modifier" if event.type == X.KeyRelease: extrastate = ReleaseModifier else: extrastate = 0 # First check for an exact modifier match match = hash_keycode(event.detail, event.state | extrastate) if self.bindings.has_key(match): self.bindings[match](event) # else, check for an AnyModifier key else: match = hash_keycode(event.detail, X.AnyModifier | extrastate) if self.bindings.has_key(match): self.bindings[match](event)
def _accept(self, ev): wmanager.debug('mozilla', 'Accepting popup') self.status_msg.pop() # Deiconify-by-moving-back... # self.client.moveresize(0, 20, self.client.width, self.client.height, 1) self.client.deiconify() self._cleanup()
def inspect_disable(self, force=0): # Inspect already disabled if self.inspect_socket is None: return if self.inspect_clients: # Shut down all clients if force: for c in self.inspect_clients.values(): c.close() # Beep and abort else: self.display.bell(50) return wmanager.debug('inspect', 'disabling inspect server') for s in self.screens: try: s.modewindow_remove_message(self.inspect_message) except AttributeError: pass self.inspect_message = None self.inspect_clients = None self.default_screen.root.delete_property(self.PLWM_INSPECT_SERVER) self.inspect_cookie = None self.inspect_socket_event.cancel() self.inspect_socket_event = None self.inspect_socket.close() self.inspect_socket = None
def view_reorder_before_move(self, method): """Internal function. Moves this view to the front of the list, if reorder is enabled and the view has been visible for long enough. Also move if the enter method differs from the previous move type. Call this in any view changing methods that steps or searches among views, to get the go-to-most-recently-visited functionality. """ if not self.view_reorder_views: return wmanager.debug('view', 'reordering, method %s', method) old_method = self.view_enter_method self.view_enter_method = method if self.view_index == 0: return now = time.time() if (now - self.view_enter_time >= self.view_reorder_delay or old_method != method): wmanager.debug( 'view', 'reordering, moving view %d from index %d to front', self.view_current.id, self.view_list.index(self.view_current)) self.view_list.remove(self.view_current) self.view_list.insert(0, self.view_current) self.view_index = 0
def comp_send_plcm_message(self, message, source, target): if self.comp_control_window is None: # Try to find the control window on demand, instead of # being fancy and waiting for events about it root = self.display.screen(0).root r = root.get_full_property(self._PLCM_CONTROL_WINDOW, Xatom.WINDOW) if r is None or r.format != 32 or len(r.value) != 1: wmanager.debug('composite', 'no plcm control window, not doing anything') return 0 self.comp_control_window = self.display.create_resource_object( 'window', r.value[0]) wmanager.debug('composite', 'plcm control window: %s', self.comp_control_window) # We knows the ID of the control window now ev = Xlib.protocol.event.ClientMessage( window=self.comp_control_window, client_type=message, data=(32, [source.id, target.id, 0, 0, 0])) self.comp_control_window.send_event( ev, onerror=self.comp_send_message_error) return 1
def __init__(self, keyhandler, event): # Always initialize the keyhandler, otherwise # we'll get problems in the __del__ method. try: keys.KeyGrabKeyboard.__init__(self, keyhandler.wm, event.time) except keys.error, status: wmanager.debug('keys', 'Grabbing keyboard failed: %d', status)
def inspect_create_new_client(self): conn, addr = self.inspect_socket.accept() wmanager.debug('inspect', 'connection from %s', addr) client = InspectClient(self, conn, addr) self.inspect_clients[client.event] = client self.inspect_set_message()
def _timeout(self, ev): wmanager.debug('mozilla', 'Rejecting popup') self.status_msg.pop() # Delete the window, unless the user already has deiconified it if not self.client.is_mapped(): self.client.delete(1) self._cleanup()
def __client_del__(self): if self.outline_windows is None: return # Drop the resources held by the outline windows wmanager.debug('outline', 'freeing windows') for w in self.outline_windows: w.destroy() self.outline_name_gc.free() self.outline_name_window.destroy()
def comp_send_message_error(self, error, request): wmanager.debug('composite', 'error when sending message to plcm: %s', error) wmanager.debug('composite', 'disabling all enabled clients') for client in self.comp_clients.iterkeys(): self.comp_remove_redirection(client) self.comp_clients = {} self.comp_control_window = None
def comp_enable_client(self, client, settings=None): """Explicitly enable composition effects for CLIENT. Normally, this is done by calling e.g. comp_change_brightness(). """ # Is client already composition enabled? if client in self.comp_clients: return # Is composition disabled due to strange visuals? if client.window._composition_disabled: wmanager.debug('composite', 'composition disabled for client %s', client) return # When render redirection is enabled for the client, it will # be unmapped and then remapped. Block those events so they # aren't misinterpreted as a withdrawal. # This is an disadvantage of splitting the main work of # composition into plcm: it would be much more natural for # plcm to handle everything there is about composition, but it # is nigh on impossible to reliably block plwm from receiving # these events then. client.window._proxy.change_attributes(event_mask=X.NoEventMask) client.event_mask.block(X.StructureNotifyMask) client.window._window.composite_redirect_window(RedirectManual) client.event_mask.unblock(X.StructureNotifyMask) client.window._proxy.change_attributes( event_mask=X.SubstructureRedirectMask) if self.comp_send_plcm_message(self._PLCM_ENABLE, client.window._window, client.window._proxy): wmanager.debug('composite', 'enabled composition for client %s', client) if settings is None: settings = ClientSettings() self.comp_clients[client] = settings else: self.comp_remove_redirection(client)
def _redraw(self): wmanager.debug('frame', 'redrawing') self._frame.clear_area() # Don't draw text in frame border g = self._frame.get_geometry() self._gc.set_clip_rectangles( 0, 0, [(self._frame_width, self._frame_width, g.width - self._extra_width, g.height - self._extra_height)], X.YXBanded) self._frame.draw_text(self._gc, self._title_x, self._title_base, self._client.get_title()) self._gc.change(clip_mask=X.NONE)
def comp_disable_client(self, client): """Explicitly disable composition effects for CLIENT. Normally, this is done by calling e.g. comp_set_brightness() with value 0. """ # Is client composition enabled? if client not in self.comp_clients: return self.comp_send_plcm_message(self._PLCM_DISABLE, client.window._window, client.window._proxy) wmanager.debug('composite', 'disabled composition for client %s', client) del self.comp_clients[client] self.comp_remove_redirection(client)
def _internal_timeout(self, ev): """Called when the timer event times out. Don't override this, override _timeout instead. """ # Call _timeout if it has been at least self.timeout # seconds since the last keypress if self.last_key_time is None \ or ev.time - self.last_key_time >= self.timeout: wmanager.debug('keys', 'timeout, last_key = %s, now = %s', self.last_key_time, ev.time) self._timeout(ev) # If not: reschedule a timeout else: wmanager.debug('keys', 'rescheduling timeout at %s', self.last_key_time + self.timeout) self.timer = event.TimerEvent(self.timer_id, at=self.last_key_time + self.timeout) self.wm.events.add_timer(self.timer)
def comp_do_set_brightness(self, client, settings, value): wmanager.debug('composite', 'changing brightness from %d to %d for %s', settings.brightness, value, client) settings.brightness = value if value != 0: # value must be unsigned if value < 0: value = struct.unpack('=L', struct.pack('=l', value))[0] client.window._proxy.change_property(self._PLCM_BRIGHTNESS, Xatom.INTEGER, 32, [value]) else: client.window._proxy.delete_property(self._PLCM_BRIGHTNESS) # No longer any settings in effect? if settings.no_effects(): self.comp_disable_client(client)
def __client_init__(self): assert self.mozpopup_keymap is not None # Recognize Netscape 6.1 popups in this manner: # They have class Mozilla-bin. # They are not WM_TRANSIENT_FOR windows. # They have _MOTIF_WM_HINTS set, where MwmHints.flags have # MWM_HINTS_DECORATIONS set, # and MwmHints.decorations not in (MWM_DECOR_ALL, 0x7e) # (struct and flags defined in MwmUtil.h) # Only act when the window was found thanks to a maprequest. if not self.from_maprequest: return if self.res_class != 'Mozilla-bin': return MWM_HINTS = self.wm.display.intern_atom("_MOTIF_WM_HINTS") WM_TRANSIENT_FOR = self.wm.display.intern_atom("WM_TRANSIENT_FOR") r = self.window.get_property(WM_TRANSIENT_FOR, Xatom.WINDOW, 0, 1) if r is not None: return r = self.window.get_property(MWM_HINTS, MWM_HINTS, 0, 5) if r is None or r.format != 32 or len(r.value) != 5: return # Check flags and decoration if r.value[0] & 2 and r.value[2] not in (1, 0x7edd): wmanager.debug('mozilla', 'detected mozilla popup') # Don't map window immediately, and install # a temporary keymap to allow activating the window self.start_iconified = 1 self.mozpopup_keymap(self) self.wm.display.bell(0)
def inspect_enable(self): # Inspection already enabled if self.inspect_socket is not None: return wmanager.debug('inspect', 'enabling inspect server') # Listen on any port on the local interfaces self.inspect_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.inspect_socket.bind(('', 0)) self.inspect_socket.listen(5) self.inspect_socket_event = event.FileEvent(InspectFileEventType, self.inspect_socket, event.FileEvent.READ) self.events.add_file(self.inspect_socket_event) # Create a authentication cookie, and store it and the # portnumber in a property on screen 0 addr, port = self.inspect_socket.getsockname() self.inspect_cookie = random.randint(0, 0x7ffffffe) self.default_screen.root.change_property(self.PLWM_INSPECT_SERVER, self.PLWM_INSPECT_SERVER, 32, [port, self.inspect_cookie]) self.inspect_clients = {} self.inspect_message = modewindow.Message(.2, modewindow.LEFT, 0, '[Inspect]') for s in self.screens: try: s.modewindow_add_message(self.inspect_message) except AttributeError: pass
def _cycle_abort(self, evt): wmanager.debug('keys', 'Aborting cycle mode') self.cycle.abort() self._cleanup()
def _cycle_end(self, evt): wmanager.debug('keys', 'Leaving cycle mode') self.cycle.end() self._cleanup()
class CycleKeys(keys.KeyGrabKeyboard): """CycleKeys is a template keyhandler for cycling clients. You should subclass it to define your own keybindings and setting the approriate client filter. The filter for the cycle is specified by the attribute _cycle_filter. This can be set to any client filter, by default `true' is used. CycleKeys defines a number of event handler methods: _cycle_next Cycle to the next client _cycle_previous Cycle to the previous client _cycle_end Finish, selecting the current client _cycle_abort Abort, reverting to the previous state (if possible) By default outline cycling is used with the CycleOutline class. This can be changed by redefining the attribute _cycle_class to any subclass of Cycle. A small CycleKeys subclass example: class MyCycleKeys(CycleKeys): _cycle_class = CycleActivate _cycle_filter = cfilter.Not(cfilter.iconified) Tab = CycleKeys._cycle_next C_Tab = CycleKeys._cycle_next S_Tab = CycleKeys._cycle_previous S_C_Tab = CycleKeys._cycle_previous Return = CycleKeys._cycle_end Escape = CycleKeys._cycle_abort To activate your cycle keys, write a keyhandler event method like this in your basic keyhandler: def C_Tab(self, evt): MyCycleKeys(self, evt) """ propagate_keys = 0 timeout = 10 _cycle_class = CycleOutline _cycle_filter = cfilter.true def __init__(self, keyhandler, event): # Always initialize the keyhandler, otherwise # we'll get problems in the __del__ method. try: keys.KeyGrabKeyboard.__init__(self, keyhandler.wm, event.time) except keys.error, status: wmanager.debug('keys', 'Grabbing keyboard failed: %d', status) # Create the cycle object, but clean up and return immediately # if no clients match the filter try: wmanager.debug('keys', 'Entering cycle mode') self.cycle = self._cycle_class(keyhandler.wm.current_screen, self._cycle_filter) except NoClientsError: wmanager.debug( 'keys', 'No clients matching cycle filter, leaving cycle mode') self._cleanup()
def __init__(self, screen, window, *args, **keys): wmanager.WindowProxyBase.__init__(self, screen, window, *args, **keys) # Create a proxy window that will contain the real window as a # clone of the source window. g = window.get_geometry() a = window.get_attributes() # Never trust an X server. The visual and depth as returned # by the methods just called can result in a BadMatch, despite # the reasonable assumption that if the client could create a # window with those specs, then we can also do it. # The reason for this sad state of affairs is OpenGL: a GLX # visual that have 24 bits of colour info _and_ 8 bits of # alpha info appears to a non-GL application (e.g. plwm) to # have depth 24, but you can't create windows of that depth on # it. An example of an application that does this is # OpenOffice 2.0, which is how I found it. # In this case, just create a window on the default visual for # the proxy and disable composition for this client (as plcm # require that the proxy and the client windows use the same # visual). self._composition_disabled = 0 ec = error.CatchError(error.BadMatch) self._proxy = screen.root.create_window(g.x, g.y, g.width, g.height, g.border_width, g.depth, a.win_class, a.visual, onerror=ec) # Check if the mismatch was triggered self._wm.display.sync() if ec.get_error(): wmanager.debug('composite', 'strange visual, disabling composition for %s', window) self._composition_disabled = 1 self._proxy = screen.root.create_window(g.x, g.y, g.width, g.height, g.border_width, X.CopyFromParent) # The proxy window must have the SubstructureRedirectMask set, so # we still get MapRequest, ConfigureRequest etc for the client window. self._proxy.change_attributes(event_mask=X.SubstructureRedirectMask) # Reparent the real window into the proxy window, blocking any # UnmapNotify that might generate screen.event_mask.block(X.SubstructureNotifyMask) window.configure(border_width=0) window.reparent(self._proxy, 0, 0) screen.event_mask.unblock(X.SubstructureNotifyMask) # Some methods can be overridden simply by only working on the proxy window self.get_geometry = self._proxy.get_geometry self.circulate = self._proxy.circulate self.reparent = self._proxy.reparent self._screen.add_proxy_window(self._proxy, window)
def __init__(self, screen, window, *args, **keys): wmanager.WindowProxyBase.__init__(self, screen, window, *args, **keys) if FrameProxy._NET_WM_STATE is None: FrameProxy._NET_WM_STATE = self._wm.display.intern_atom( "_NET_WM_STATE") FrameProxy._NET_WM_STATE_DEMANDS_ATTENTION = self._wm.display.intern_atom( "_NET_WM_STATE_DEMANDS_ATTENTION") self._font = self._wm.get_font(self._title_font) self._title_pixel = self._screen.get_color(self._title_color) self._unfocused_pixel = self._screen.get_color(self._unfocused_color) self._focused_pixel = self._screen.get_color(self._focused_color) self._urgent_pixel = self._screen.get_color(self._urgent_color) fq = self._font.query() self._extra_height = fq.font_ascent + fq.font_descent + 3 * self._frame_width self._extra_width = 2 * self._frame_width self._title_x = self._frame_width self._title_y = self._frame_width self._title_base = self._frame_width + fq.font_ascent self._client_x = self._frame_width self._client_y = fq.font_ascent + fq.font_descent + 2 * self._frame_width self._focused = False # Create a proxy window for the frame that will contain the # real window g = window.get_geometry() self._frame = self._screen.root.create_window( g.x - self._client_x - g.border_width, g.y - self._client_y - g.border_width, g.width + self._extra_width, g.height + self._extra_height, 0, X.CopyFromParent, X.InputOutput, X.CopyFromParent, background_pixel=self._unfocused_pixel, event_mask=X.ExposureMask | X.SubstructureRedirectMask) wmanager.debug('frame', 'created frame %s for client %s', self._frame, self._window) self._gc = self._frame.create_gc(foreground=self._title_pixel, font=self._font) # Reparent the real window into the frame window, blocking any # UnmapNotify that might generate screen.event_mask.block(X.SubstructureNotifyMask) window.configure(border_width=0) window.reparent(self._frame, self._client_x, self._client_y) screen.event_mask.unblock(X.SubstructureNotifyMask) # Some methods can be overridden simply by only working on the proxy window self.get_geometry = self._frame.get_geometry self.circulate = self._frame.circulate self.reparent = self._frame.reparent self._screen.add_proxy_window(self._frame, window)
class InspectClient: def __init__(self, wm, sock, addr): self.wm = wm self.socket = sock self.addr = addr self.authed = 0 self.event = event.FileEvent(InspectFileEventType, self.socket, event.FileEvent.READ) self.wm.events.add_file(self.event) self.recv_len = 0 self.recv_buf = '' self.send_buf = '' self.globals = __builtins__.copy() self.globals['wm'] = self.wm def handle_file_event(self, evt): if evt.state & event.FileEvent.READ: try: d = self.socket.recv(500) except socket.error, err: wmanager.debug('inspect', 'client %s closed on failed recv: %s', self.addr, err) self.close() return if not d: wmanager.debug('inspect', 'client %s closed', self.addr) self.close() return self.recv_buf = self.recv_buf + d # First four bytes sent must the the authentication cookie if not self.authed: if len(self.recv_buf) >= 4: cookie = struct.unpack('>l', self.recv_buf[:4])[0] self.recv_buf = self.recv_buf[4:] if cookie == self.wm.inspect_cookie: self.authed = 1 self.output('Welcome to PLWM at %s\n' % self.wm.display.get_display_name()) else: wmanager.debug('inspect', 'client %s closed on wrong cookie: %d', self.addr, cookie) self.close() return while 1: # No length recieved yet, parse a little-endian fourbyte length if self.recv_len == 0: if len(self.recv_buf) >= 4: self.recv_len = struct.unpack('>l', self.recv_buf[:4])[0] # Do sanity check on length, abort connection if it is < 0 if self.recv_len < 0: wmanager.debug( 'inspect', 'client %s closed, sent negative length: %d', self.addr, self.recv_len) self.close() return self.recv_buf = self.recv_buf[4:] else: break # All data of expression read, execute it if self.recv_len <= len(self.recv_buf): data = self.recv_buf[:self.recv_len] self.recv_buf = self.recv_buf[self.recv_len:] self.recv_len = 0 self.exec_data(data) else: break if evt.state & event.FileEvent.WRITE: # Send any unsent data try: n = self.socket.send(self.send_buf) except socket.error, err: wmanager.debug('inspect', 'client %s closed on failed send: %s', self.addr, err) self.close() return self.send_buf = self.send_buf[n:] # If there are no data left to send, clear the # WRITE flag in the event to avoid a lot of # select follies. if len(self.send_buf) == 0: evt.set_mode(clear=event.FileEvent.WRITE)
def handle_file_event(self, evt): if evt.state & event.FileEvent.READ: try: d = self.socket.recv(500) except socket.error, err: wmanager.debug('inspect', 'client %s closed on failed recv: %s', self.addr, err) self.close() return if not d: wmanager.debug('inspect', 'client %s closed', self.addr) self.close() return self.recv_buf = self.recv_buf + d # First four bytes sent must the the authentication cookie if not self.authed: if len(self.recv_buf) >= 4: cookie = struct.unpack('>l', self.recv_buf[:4])[0] self.recv_buf = self.recv_buf[4:] if cookie == self.wm.inspect_cookie: self.authed = 1 self.output('Welcome to PLWM at %s\n' % self.wm.display.get_display_name()) else: wmanager.debug('inspect', 'client %s closed on wrong cookie: %d', self.addr, cookie) self.close() return while 1: # No length recieved yet, parse a little-endian fourbyte length if self.recv_len == 0: if len(self.recv_buf) >= 4: self.recv_len = struct.unpack('>l', self.recv_buf[:4])[0] # Do sanity check on length, abort connection if it is < 0 if self.recv_len < 0: wmanager.debug( 'inspect', 'client %s closed, sent negative length: %d', self.addr, self.recv_len) self.close() return self.recv_buf = self.recv_buf[4:] else: break # All data of expression read, execute it if self.recv_len <= len(self.recv_buf): data = self.recv_buf[:self.recv_len] self.recv_buf = self.recv_buf[self.recv_len:] self.recv_len = 0 self.exec_data(data) else: break
def __init__(self, obj, deprecated_arg=None): """Instantiate a KeyHandler object. """ # Warn if the deprecated and unused former "dispatch" argument # is used. if deprecated_arg is not None: sys.stderr.write( "%s: warning: using deprecated KeyHandler __init__ interface\n" % sys.argv[0]) wmanager.debug('mem', 'Initing keyhandler %s for %s', self, obj) # Figure out if we have been added to a WindowManager, Screen # or Client object. Set up KeygrabManager objects if not # already done on screens or clients. if isinstance(obj, wmanager.WindowManager): wm = obj grabmgrs = [] for s in obj.screens: if not hasattr(s, 'keygrab_mgr'): s.keygrab_mgr = KeygrabManager(obj, s.root) grabmgrs.append(s.keygrab_mgr) elif isinstance(obj, wmanager.Screen): wm = obj.wm if not hasattr(obj, 'keygrab_mgr'): obj.keygrab_mgr = KeygrabManager(obj.wm, obj.root) grabmgrs = [obj.keygrab_mgr] elif isinstance(obj, wmanager.Window): wm = obj.wm if not hasattr(obj, 'keygrab_mgr'): obj.keygrab_mgr = KeygrabManager(obj.wm, obj.window) grabmgrs = [obj.keygrab_mgr] else: raise TypeError('expected WindowManager, Screen or Client object') # Dig through all names in this object, ignoring those beginning with # an underscore. # First collect all method names in this and it's base classes names = {} c = [self.__class__] while len(c): names.update(c[0].__dict__) c = c + list(c[0].__bases__) del c[0] names.update(self.__dict__) # And now parse the names rawbinds = [] for name in names.keys(): if name[0] != '_': # Find modifiers in name mask = 0 parts = string.split(name, '_') while len(parts) >= 2 and modifiers.has_key(parts[0]): mask = mask | modifiers[parts[0]] del parts[0] # Find name keysym rest = string.join(parts, '_') keysym = XK.string_to_keysym(rest) if keysym != X.NoSymbol: rawbinds.append((keysym, mask, getattr(self, name))) self.wm = wm self.dispatch = obj.dispatch self.grabmgrs = grabmgrs self.rawbindings = rawbinds self.grabs = [] # Add handlers if self.propagate_keys: self.dispatch.add_handler(X.KeyPress, self._keyevent, handlerid=self) self.dispatch.add_handler(X.KeyRelease, self._keyevent, handlerid=self) else: self.dispatch.add_grab_handler(X.KeyPress, self._keyevent, handlerid=self) self.dispatch.add_grab_handler(X.KeyRelease, self._keyevent, handlerid=self) # Okay, so we will remap and regrab once for every # screen, but I'm not worried. xmodmap isn't the most # frequently used command... self.dispatch.add_handler(X.MappingNotify, self._mappingnotify, handlerid=self) if self.timeout: self.last_key_time = None self.timer_id = event.new_event_type() self.timer = event.TimerEvent(self.timer_id, after=self.timeout) self.wm.events.add_timer(self.timer) self.dispatch.add_handler(self.timer_id, self._internal_timeout, handlerid=self) self._buildmap()
def __del__(self): wmanager.debug('mem', 'Freeing keyhandler %s', self) self._cleanup()
def focus_leave(self, evt): wmanager.debug('focus', 'Pointer leave %s', evt.window) self.set_current_client(self.ptfocus_get_focused_client(), evt.time)