def setup(): """Setup the system tray. Initializes tray support by requesting the appropriate _NET_SYSTEM_TRAY atom for the X11 display we are running on, then acquiring the selection for this atom. Afterwards, tray clients will send ClientMessages to our window. """ # We need a window to own the selection global selection_window selection_window = pwm.windows.create( -1, -1, 1, 1, xcb.mask((xcb.CW_OVERRIDE_REDIRECT, 1))) xcb.core.map_window(selection_window) # Get the selection global tray_atom tray_atom = pwm.atom.get("_NET_SYSTEM_TRAY_S{}".format( xcb.screen_number)) xcb.core.set_selection_owner(selection_window, tray_atom, xcb.CURRENT_TIME) # Inform clients waiting for a new _NET_SYSTEM_TRAY that we took the # selection. event = pwm.windows.create_client_message( xcb.screen.root, pwm.atom.get("MANAGER"), xcb.CURRENT_TIME, tray_atom, selection_window) xcb.core.send_event(False, xcb.screen.root, 0xffffff, event)
def create_window(self): mask = [(xcb.CW_OVERRIDE_REDIRECT, 1), (xcb.CW_BACK_PIXEL, color.get_pixel(config.bar.background)), (xcb.CW_EVENT_MASK, xcb.EVENT_MASK_EXPOSURE)] return pwm.windows.create(self.x, self.y, self.width, self.height, xcb.mask(mask))
def setup(): """Setup the system tray. Initializes tray support by requesting the appropriate _NET_SYSTEM_TRAY atom for the X11 display we are running on, then acquiring the selection for this atom. Afterwards, tray clients will send ClientMessages to our window. """ # We need a window to own the selection global selection_window selection_window = pwm.windows.create( -1, -1, 1, 1, xcb.mask((xcb.CW_OVERRIDE_REDIRECT, 1))) xcb.core.map_window(selection_window) # Get the selection global tray_atom tray_atom = pwm.atom.get("_NET_SYSTEM_TRAY_S{}".format(xcb.screen_number)) xcb.core.set_selection_owner(selection_window, tray_atom, xcb.CURRENT_TIME) # Inform clients waiting for a new _NET_SYSTEM_TRAY that we took the # selection. event = pwm.windows.create_client_message(xcb.screen.root, pwm.atom.get("MANAGER"), xcb.CURRENT_TIME, tray_atom, selection_window) xcb.core.send_event(False, xcb.screen.root, 0xffffff, event)
def setup(): global _width _width = xcb.screen.width_in_pixels global _height _height = pwm.bar.calculate_height() global _window mask = [(xcb.CW_OVERRIDE_REDIRECT, 1), (xcb.CW_BACK_PIXEL, pwm.color.get_pixel(config.bar.background)), (xcb.CW_EVENT_MASK, xcb.EVENT_MASK_EXPOSURE)] _window = pwm.windows.create(pwm.bar.primary.x, pwm.bar.primary.y, _width, _height, xcb.mask(mask)) global _pixmap _pixmap = xcb.core.generate_id() xcb.core.create_pixmap( xcb.screen.root_depth, _pixmap, _window, _width, _height) global _gc _gc = xcb.core.generate_id() xcb.core.create_gc(_gc, xcb.screen.root, *xcb.mask([(xcb.GC_FOREGROUND, xcb.screen.white_pixel), (xcb.GC_BACKGROUND, xcb.screen.black_pixel), (xcb.GC_GRAPHICS_EXPOSURES, 0)])) global _surface _surface = cairo.xcb_surface_create( xcb.conn, _pixmap, xcb.aux_find_visual_by_id(xcb.screen, xcb.screen.root_visual), _width, _height) global _ctx _ctx = cairo.create(_surface) _ctx.select_font_face(config.bar.font.face, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL) _ctx.set_font_size(config.bar.font.size)
def create_gc(self): gc = xcb.core.generate_id() xcb.core.create_gc( gc, xcb.screen.root, *xcb.mask([(xcb.GC_FOREGROUND, xcb.screen.white_pixel), (xcb.GC_BACKGROUND, xcb.screen.black_pixel), (xcb.GC_GRAPHICS_EXPOSURES, 0)])) return gc
def set_cursor(cursor): fid = xcb.core.generate_id() xcb.core.open_font(fid, len("cursor"), "cursor".encode("UTF-8")) cid = xcb.core.generate_id() xcb.core.create_glyph_cursor(cid, fid, fid, cursor, cursor + 1, 0, 0, 0, 65535, 65535, 65535) xcb.core.change_window_attributes(xcb.screen.root, *xcb.mask((xcb.CW_CURSOR, cid))) xcb.core.free_cursor(cid) xcb.core.close_font(fid)
def configure_clients(): offset = 0 for client, mapped in clients.items(): if not mapped: continue # Client windows are squares, width=height offset += pwm.bar.primary.height+2 xcb.core.configure_window( client, *xcb.mask((xcb.CONFIG_WINDOW_X, pwm.bar.primary.width-offset))) pwm.bar.primary.update_systray(offset)
def configure_clients(): offset = 0 for client, mapped in clients.items(): if not mapped: continue # Client windows are squares, width=height offset += pwm.bar.primary.height + 2 xcb.core.configure_window( client, *xcb.mask((xcb.CONFIG_WINDOW_X, pwm.bar.primary.width - offset))) pwm.bar.primary.update_systray(offset)
def configure(wid, **kwargs): """Configure the window and set the given variables in relation to the workspace. Arguments can be: x, y, width, height, stackmode If absolute=True then the window will be configured in absolute coordinates and not in relation to the workspace. """ workspace = pwm.workspaces.current() values = [] abs_ = 0 if kwargs.get("absolute", False) else 1 border = (kwargs["borderwidth"] if "borderwidth" in kwargs else config.window.border) values.append((xcb.CONFIG_WINDOW_BORDER_WIDTH, border)) # We need to cast x and y in order to have correct handling for negative # values. if "x" in kwargs: values.append( (xcb.CONFIG_WINDOW_X, xcb.ffi.cast("uint32_t", int(workspace.x*abs_ + kwargs["x"])))) if "y" in kwargs: values.append( (xcb.CONFIG_WINDOW_Y, xcb.ffi.cast("uint32_t", int(workspace.y*abs_ + kwargs["y"])))) if "width" in kwargs: values.append((xcb.CONFIG_WINDOW_WIDTH, max(0, int(kwargs["width"] - 2*border)))) if "height" in kwargs: values.append((xcb.CONFIG_WINDOW_HEIGHT, max(0, int(kwargs["height"] - 2*border)))) if "stackmode" in kwargs: values.append((xcb.CONFIG_WINDOW_STACK_MODE, kwargs["stackmode"])) if "sibling" in kwargs: values.append((xcb.CONFIG_WINDOW_SIBLING, kwargs["sibling"])) xcb.core.configure_window(wid, *xcb.mask(values)) if wid in managed and "noupdate" not in kwargs: if ("x" in kwargs or "y" in kwargs or "width" in kwargs or "height" in kwargs): update_geometry(wid)
def setup(): """Setup the root window.""" cookie = xcb.core.change_window_attributes_checked( xcb.screen.root, *xcb.mask([(xcb.CW_EVENT_MASK, EVENT_MASK)])) cookie.check() try: # We have to set the cursor now, otherwise it will not show up until # the first client is launched. set_cursor(Cursor.left_ptr) except: logging.exception("Root cursor error") try: _set_properties() except: logging.exception("Root properties error")
def create(x, y, width, height, mask=None): """Create a new window and return its id.""" wid = xcb.core.generate_id() if not mask: mask = xcb.mask([(xcb.CW_BACK_PIXEL, xcb.screen.black_pixel), (xcb.CW_EVENT_MASK, xcb.EVENT_MASK_EXPOSURE)]) xcb.core.create_window( xcb.screen.root_depth, wid, xcb.screen.root, x, y, width, height, 0, # border xcb.WINDOW_CLASS_INPUT_OUTPUT, xcb.screen.root_visual, *mask) return wid
def handle_client_message(event): """Handle client messages directed at the systray.""" # System Tray Specification # http://standards.freedesktop.org/systemtray-spec/latest # # The first data field in the message is a timestamp (the stamp of the # current event, if available, otherwise CurrentTime). The second data # field is an integer indicating the op code of the message. # The content remaining three data fields depends on the type of message # being sent. # To begin the docking process, the tray icon application sends a # client message event to the manager selection owner window, [...]. # This event should contain the SYSTEM_TRAY_REQUEST_DOCK opcode, # xclient.data.l[2] should contain the X window ID of the tray icon to be # docked. # At this point the "embedding life cycle" explained in the XEMBED # specification begins. The XEMBED specification explains how the # embedding application will interact with the embedded tray icon, and how # the embedder/embedded relationship may be ended. if event.data.data32[1] == SYSTEM_TRAY_REQUEST_DOCK: client = event.data.data32[2] logging.debug("client {} requested docking".format(client)) # Listen for PropertyNotify events to get the most recent value of # the XEMBED_MAPPED atom, also listen for UnmapNotify events pwm.windows.change_attributes( client, (xcb.CW_EVENT_MASK, xcb.EVENT_MASK_PROPERTY_CHANGE | xcb.EVENT_MASK_STRUCTURE_NOTIFY)) # Request the _XEMBED_INFO property. The XEMBED specification # says this *has* to be set, but VLC does not set it... xe_version = 1 map_it = True info = pwm.windows.get_property(client, "_XEMBED_INFO") if info: xe_version = info[0] map_it = ((info[1] & XEMBED_MAPPED) == XEMBED_MAPPED) logging.debug("xembed version: {}".format(info[0])) logging.debug("xembed flags: {}".format(info[1])) else: logging.debug("client {} violates the XEMBED protocol, " "_XEMBED_INFO not set.".format(client)) # Put the client inside the save set. Upon termination (whether killed # or normal exit does not matter), these clients will be correctly # reparented to their most closest living ancestor. Without this, tray # icons might die when we exit/crash. xcb.core.change_save_set(xcb.SET_MODE_INSERT, client) xcb.core.reparent_window(client, pwm.bar.primary.wid, 0, 0) # We reconfigure the window to use a reasonable size. The systray # specification explicitly says: # Tray icons may be assigned any size by the system tray, and # should do their best to cope with any size effectively size = pwm.bar.primary.height xcb.core.configure_window( client, *xcb.mask([(xcb.CONFIG_WINDOW_WIDTH, size), (xcb.CONFIG_WINDOW_HEIGHT, size)])) # Send the XEMBED_EMBEDDED_NOTIFY message. # http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html # An XEmbed message is an X11 client message with message type # "_XEMBED". The format is 32, the first three data longs carry the # toolkit's X time (l[0]), the message's major opcode (l[1]) and the # message's detail code (l[2]). If no detail is required, the value # passed has to be 0. The remaining two data longs (l[3] and l[4]) are # reserved for data1 and data2. Unused bytes of the client message are # set to 0. The event is sent to the target window with no event mask # and propagation turned off. event = pwm.windows.create_client_message( client, pwm.atom.get("_XEMBED"), xcb.CURRENT_TIME, XEMBED_EMBEDDED_NOTIFY, 0, pwm.bar.primary.wid, xe_version) xcb.core.send_event(False, client, xcb.EVENT_MASK_NO_EVENT, event) if map_it: xcb.core.map_window(client) clients[client] = map_it configure_clients()
def change_attributes(wid, masks): """Set attributes for the given window.""" xcb.core.change_window_attributes(wid, *xcb.mask(masks))
def handle_client_message(event): """Handle client messages directed at the systray.""" # System Tray Specification # http://standards.freedesktop.org/systemtray-spec/latest # # The first data field in the message is a timestamp (the stamp of the # current event, if available, otherwise CurrentTime). The second data # field is an integer indicating the op code of the message. # The content remaining three data fields depends on the type of message # being sent. # To begin the docking process, the tray icon application sends a # client message event to the manager selection owner window, [...]. # This event should contain the SYSTEM_TRAY_REQUEST_DOCK opcode, # xclient.data.l[2] should contain the X window ID of the tray icon to be # docked. # At this point the "embedding life cycle" explained in the XEMBED # specification begins. The XEMBED specification explains how the # embedding application will interact with the embedded tray icon, and how # the embedder/embedded relationship may be ended. if event.data.data32[1] == SYSTEM_TRAY_REQUEST_DOCK: client = event.data.data32[2] logging.debug("client {} requested docking".format(client)) # Listen for PropertyNotify events to get the most recent value of # the XEMBED_MAPPED atom, also listen for UnmapNotify events pwm.windows.change_attributes( client, (xcb.CW_EVENT_MASK, xcb.EVENT_MASK_PROPERTY_CHANGE | xcb.EVENT_MASK_STRUCTURE_NOTIFY)) # Request the _XEMBED_INFO property. The XEMBED specification # says this *has* to be set, but VLC does not set it... xe_version = 1 map_it = True info = pwm.windows.get_property(client, "_XEMBED_INFO") if info: xe_version = info[0] map_it = ((info[1] & XEMBED_MAPPED) == XEMBED_MAPPED) logging.debug("xembed version: {}".format(info[0])) logging.debug("xembed flags: {}".format(info[1])) else: logging.debug("client {} violates the XEMBED protocol, " "_XEMBED_INFO not set.".format(client)) # Put the client inside the save set. Upon termination (whether killed # or normal exit does not matter), these clients will be correctly # reparented to their most closest living ancestor. Without this, tray # icons might die when we exit/crash. xcb.core.change_save_set(xcb.SET_MODE_INSERT, client) xcb.core.reparent_window(client, pwm.bar.primary.wid, 0, 0) # We reconfigure the window to use a reasonable size. The systray # specification explicitly says: # Tray icons may be assigned any size by the system tray, and # should do their best to cope with any size effectively size = pwm.bar.primary.height xcb.core.configure_window( client, *xcb.mask([(xcb.CONFIG_WINDOW_WIDTH, size), (xcb.CONFIG_WINDOW_HEIGHT, size)])) # Send the XEMBED_EMBEDDED_NOTIFY message. # http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html # An XEmbed message is an X11 client message with message type # "_XEMBED". The format is 32, the first three data longs carry the # toolkit's X time (l[0]), the message's major opcode (l[1]) and the # message's detail code (l[2]). If no detail is required, the value # passed has to be 0. The remaining two data longs (l[3] and l[4]) are # reserved for data1 and data2. Unused bytes of the client message are # set to 0. The event is sent to the target window with no event mask # and propagation turned off. event = pwm.windows.create_client_message(client, pwm.atom.get("_XEMBED"), xcb.CURRENT_TIME, XEMBED_EMBEDDED_NOTIFY, 0, pwm.bar.primary.wid, xe_version) xcb.core.send_event(False, client, xcb.EVENT_MASK_NO_EVENT, event) if map_it: xcb.core.map_window(client) clients[client] = map_it configure_clients()