diff --git a/docs/_static/images/max_solid_edge.png b/docs/_static/images/max_solid_edge.png new file mode 100644 index 00000000..e8e7049e Binary files /dev/null and b/docs/_static/images/max_solid_edge.png differ diff --git a/qtile_extras/layout/decorations/__init__.py b/qtile_extras/layout/decorations/__init__.py index 7bac7666..f0fad602 100644 --- a/qtile_extras/layout/decorations/__init__.py +++ b/qtile_extras/layout/decorations/__init__.py @@ -23,6 +23,7 @@ GradientBorder, GradientFrame, ScreenGradientBorder, + SolidEdge, ) diff --git a/qtile_extras/layout/decorations/borders.py b/qtile_extras/layout/decorations/borders.py index 280f7b43..71d9c9b0 100644 --- a/qtile_extras/layout/decorations/borders.py +++ b/qtile_extras/layout/decorations/borders.py @@ -18,6 +18,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import cairocffi +import xcffib.xproto from libqtile import qtile from libqtile.configurable import Configurable from libqtile.confreader import ConfigError @@ -25,9 +26,11 @@ try: from libqtile.backend.wayland._ffi import ffi, lib + from libqtile.backend.wayland.window import _rgb from wlroots import ffi as wlr_ffi from wlroots import lib as wlr_lib from wlroots.wlr_types import Buffer, SceneBuffer + from wlroots.wlr_types.scene import SceneRect HAS_WAYLAND = True except (ImportError, ModuleNotFoundError): @@ -77,11 +80,20 @@ def _new_buffer(self): return image_buffer, surface + def _get_edges(self, bw, x, y, width, height): + return [ + (x, y, width, bw), + (self.outer_w - bw - x, bw + y, bw, height - 2 * bw), + (x, self.outer_h - y - bw, width, bw), + (x, bw + y, bw, height - bw * 2), + ] + def _x11_draw( self, window, depth, pixmap, gc, outer_w, outer_h, borderwidth, x, y, width, height ): self.visual = window.get_attributes().visual self.window = window + self.core = window.conn.conn.core self.wid = window.wid self.depth = depth self.pixmap = pixmap @@ -106,33 +118,28 @@ def _wayland_draw(self, window, outer_w, outer_h, borderwidth, x, y, width, heig self.wid = window.wid self.outer_w = outer_w self.outer_h = outer_h - bw = borderwidth - self.rects = [ - (x, y, width, bw), - (outer_w - bw - x, bw + y, bw, height - 2 * bw), - (x, outer_h - y - bw, width, bw), - (x, bw + y, bw, height - bw * 2), - ] + self.rects = self._get_edges(borderwidth, x, y, width, height) if self.needs_surface: image_buffer, surface = self._new_buffer() else: image_buffer = None surface = None - self.wayland_draw(borderwidth, x, y, width, height, surface) - - scenes = [] - for x, y, w, h in self.rects: - scene_buffer = SceneBuffer.create(self.window.container, Buffer(image_buffer)) - scene_buffer.node.set_position(x, y) - wlr_lib.wlr_scene_buffer_set_dest_size(scene_buffer._ptr, w, h) - fbox = wlr_ffi.new("struct wlr_fbox *") - fbox.x = x - fbox.y = y - fbox.width = w - fbox.height = h - wlr_lib.wlr_scene_buffer_set_source_box(scene_buffer._ptr, fbox) - scenes.append(scene_buffer) + scenes = self.wayland_draw(borderwidth, x, y, width, height, surface) + + if self.needs_surface: + scenes = [] + for x, y, w, h in self.rects: + scene_buffer = SceneBuffer.create(self.window.container, Buffer(image_buffer)) + scene_buffer.node.set_position(x, y) + wlr_lib.wlr_scene_buffer_set_dest_size(scene_buffer._ptr, w, h) + fbox = wlr_ffi.new("struct wlr_fbox *") + fbox.x = x + fbox.y = y + fbox.width = w + fbox.height = h + wlr_lib.wlr_scene_buffer_set_source_box(scene_buffer._ptr, fbox) + scenes.append(scene_buffer) return scenes, image_buffer, surface @@ -357,3 +364,48 @@ def pos(point): ctx.set_source(gradient) ctx.paint() ctx.restore() + + +class SolidEdge(_BorderStyle): + """ + A decoration that renders a solid border. Colours can be specified for + each edge. + """ + + _screenshots = [("max_solid_edge.png", 'SolidEdge(colours=["00f", "0ff", "00f", "0ff"])')] + + needs_surface = False + + defaults = [ + ( + "colours", + ["00f", "00f", "00f", "00f"], + "List of colours for each edge of the window [N, E, S, W].", + ) + ] + + def __init__(self, **config): + _BorderStyle.__init__(self, **config) + self.add_defaults(SolidEdge.defaults) + + if not isinstance(self.colours, (list, tuple)) and len(self.colours != 4): + raise ConfigError("colours must have 4 values.") + + def x11_draw(self, borderwidth, x, y, width, height, surface): + edges = self._get_edges(borderwidth, x, y, width, height) + for (x, y, w, h), c in zip(edges, self.colours): + self.core.ChangeGC( + self.gc, xcffib.xproto.GC.Foreground, [self.window.conn.color_pixel(c)] + ) + rect = xcffib.xproto.RECTANGLE.synthetic(x, y, w, h) + self.core.PolyFillRectangle(self.pixmap, self.gc, 1, [rect]) + + def wayland_draw(self, borderwidth, x, y, width, height, surface): + scene_rects = [] + edges = self._get_edges(borderwidth, x, y, width, height) + for (x, y, w, h), c in zip(edges, self.colours): + rect = SceneRect(self.window.container, w, h, _rgb(c)) + rect.node.set_position(x, y) + scene_rects.append(rect) + + return scene_rects