Skip to content

Commit

Permalink
core: use ScreenRect everywhere we represent a screen
Browse files Browse the repository at this point in the history
We had at least three different classes (all dataclasses without the name)
that represented a screen, and additionally open coded them with tuple
unpacking in a few places as well. Let's consolidate on one class, use
dataclass so we get free __init__ and __repr__, and use it everywhere.

For now this still just contains xywh, but it is where we will eventually
add the serial number for the output, and could in the future carry
information about DPI, etc.

I don't love the name ScreenRect for this, but this might (?) be user
facing, and I don't have a better name, so whatever...

Signed-off-by: Tycho Andersen <tycho@tycho.pizza>
  • Loading branch information
tych0 committed May 20, 2024
1 parent 41d5d0e commit ca7b28e
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 85 deletions.
3 changes: 2 additions & 1 deletion libqtile/backend/base/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from abc import ABCMeta, abstractmethod

from libqtile.command.base import CommandObject, expose_command
from libqtile.config import ScreenRect

if typing.TYPE_CHECKING:
from typing import Any
Expand Down Expand Up @@ -54,7 +55,7 @@ def update_desktops(self, groups: list[_Group], index: int) -> None:
"""Set the current desktops of the window manager"""

@abstractmethod
def get_screen_info(self) -> list[tuple[int, int, int, int]]:
def get_screen_info(self) -> list[ScreenRect]:
"""Get the screen information"""

@abstractmethod
Expand Down
7 changes: 4 additions & 3 deletions libqtile/backend/wayland/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
from libqtile.backend.wayland import inputs, layer, window, wlrq, xdgwindow, xwindow
from libqtile.backend.wayland.output import Output
from libqtile.command.base import expose_command
from libqtile.config import ScreenRect
from libqtile.log_utils import logger

try:
Expand Down Expand Up @@ -497,7 +498,7 @@ def _on_new_output(self, _listener: Listener, wlr_output: wlrOutput) -> None:
if not self._current_output:
self._current_output = output
self.cursor.set_xcursor(self._cursor_manager, "default")
box = Box(*output.get_geometry())
box = Box(*output.get_screen_info())
x = box.x + box.width / 2
y = box.y + box.height / 2
self.warp_pointer(x, y)
Expand Down Expand Up @@ -1414,9 +1415,9 @@ def _under_pointer(self) -> tuple[window.WindowType, Surface | None, float, floa
logger.warning("Couldn't determine what was under the pointer. Please report.")
return None

def get_screen_info(self) -> list[tuple[int, int, int, int]]:
def get_screen_info(self) -> list[ScreenRect]:
"""Get the output information"""
return [output.get_geometry() for output in self.outputs]
return [output.get_screen_info() for output in self.outputs]

def grab_key(self, key: config.Key | config.KeyChord) -> tuple[int, int]:
"""Configure the backend to grab the key event"""
Expand Down
8 changes: 5 additions & 3 deletions libqtile/backend/wayland/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from wlroots.wlr_types.output import CustomMode

from libqtile.backend.wayland.wlrq import HasListeners
from libqtile.config import ScreenRect
from libqtile.log_utils import logger

if TYPE_CHECKING:
Expand Down Expand Up @@ -98,7 +99,8 @@ def finalize(self) -> None:
self.scene_output.destroy()

def __repr__(self) -> str:
return "<Output (%s, %s, %s, %s)>" % self.get_geometry()
i = self.get_screen_info()
return f"<Output ({i.x}, {i.y}, {i.width}, {i.height})>"

@property
def screen(self) -> Screen:
Expand Down Expand Up @@ -131,9 +133,9 @@ def _on_request_state(self, _listener: Listener, request: OutputEventRequestStat
logger.debug("Signal: output request_state")
self.wlr_output.commit(request.state)

def get_geometry(self) -> tuple[int, int, int, int]:
def get_screen_info(self) -> ScreenRect:
width, height = self.wlr_output.effective_resolution()
return int(self.x), int(self.y), width, height
return ScreenRect(int(self.x), int(self.y), width, height)

def organise_layers(self) -> None:
"""Organise the positioning of layer shell surfaces."""
Expand Down
19 changes: 4 additions & 15 deletions libqtile/backend/x11/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from libqtile.backend import base
from libqtile.backend.x11 import window, xcbq
from libqtile.backend.x11.xkeysyms import keysyms
from libqtile.config import ScreenRect
from libqtile.log_utils import logger
from libqtile.utils import QtileError

Expand Down Expand Up @@ -183,23 +184,11 @@ def finalize(self) -> None:
delattr(self, "qtile")
self.conn.finalize()

def get_screen_info(self) -> list[tuple[int, int, int, int]]:
info = [(s.x, s.y, s.width, s.height) for s in self.conn.pseudoscreens]

if not info:
info.append(
(
0,
0,
self.conn.default_screen.width_in_pixels,
self.conn.default_screen.height_in_pixels,
)
)

def get_screen_info(self) -> list[ScreenRect]:
ps = self.conn.pseudoscreens
if self.qtile:
self._xpoll()

return info
return ps

@property
def wmname(self):
Expand Down
50 changes: 12 additions & 38 deletions libqtile/backend/x11/xcbq.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
from libqtile.backend.x11 import window
from libqtile.backend.x11.xcursors import Cursors
from libqtile.backend.x11.xkeysyms import keysyms
from libqtile.config import ScreenRect
from libqtile.log_utils import logger
from libqtile.utils import QtileError, hex

Expand Down Expand Up @@ -361,20 +362,6 @@ def get_visual_for_depth(screen, depth):
return i.visuals[0]


class PseudoScreen:
"""
This may be a Xinerama screen or a RandR CRTC, both of which are
rectangular sections of an actual Screen.
"""

def __init__(self, conn, x, y, width, height):
self.conn = conn
self.x = x
self.y = y
self.width = width
self.height = height


class Colormap:
def __init__(self, conn, cid):
self.conn = conn
Expand Down Expand Up @@ -418,17 +405,13 @@ def __init__(self, conn):
self.ext.SelectInput(conn.default_screen.root.wid, xcffib.randr.NotifyMask.ScreenChange)

def query_crtcs(self, root):
crtc_list = []
for crtc in self.ext.GetScreenResources(root).reply().crtcs:
crtc_info = self.ext.GetCrtcInfo(crtc, xcffib.CurrentTime).reply()
crtc_dict = {
"x": crtc_info.x,
"y": crtc_info.y,
"width": crtc_info.width,
"height": crtc_info.height,
}
crtc_list.append(crtc_dict)
return crtc_list
infos = []
for output in self.ext.GetScreenResources(root).reply().outputs:
output_info = self.ext.GetOutputInfo(output, xcffib.CurrentTime).reply()
crtc_info = self.ext.GetCrtcInfo(output_info.crtc, xcffib.CurrentTime).reply()

infos.append(ScreenRect(crtc_info.x, crtc_info.y, crtc_info.width, crtc_info.height))
return infos


class XFixes:
Expand Down Expand Up @@ -499,28 +482,19 @@ def colormap(self, desired_depth):

@property
def pseudoscreens(self):
pseudoscreens = []
if hasattr(self, "xinerama"):
pseudoscreens = []
for i, s in enumerate(self.xinerama.query_screens()):
scr = PseudoScreen(
self,
scr = ScreenRect(
s.x_org,
s.y_org,
s.width,
s.height,
)
pseudoscreens.append(scr)
return pseudoscreens
elif hasattr(self, "randr"):
for i in self.randr.query_crtcs(self.screens[0].root.wid):
scr = PseudoScreen(
self,
i["x"],
i["y"],
i["width"],
i["height"],
)
pseudoscreens.append(scr)
return pseudoscreens
return self.randr.query_crtcs(self.screens[0].root.wid)

def finalize(self):
self.cursors.finalize()
Expand Down
20 changes: 6 additions & 14 deletions libqtile/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import os.path
import re
import sys
from dataclasses import dataclass
from typing import TYPE_CHECKING

from libqtile import configurable, hook, utils
Expand Down Expand Up @@ -372,21 +373,12 @@ def __init__(self, btndef: str, *commands: LazyCall, start: LazyCall | None = No
super().__init__(modkeys, button, *commands, start=start)


@dataclass
class ScreenRect:
def __init__(self, x: int, y: int, width: int, height: int) -> None:
self.x = x
self.y = y
self.width = width
self.height = height

def __repr__(self) -> str:
return "<%s %d,%d %d,%d>" % (
self.__class__.__name__,
self.x,
self.y,
self.width,
self.height,
)
x: int
y: int
width: int
height: int

def hsplit(self, columnwidth: int) -> tuple[ScreenRect, ScreenRect]:
assert 0 < columnwidth < self.width
Expand Down
33 changes: 22 additions & 11 deletions libqtile/core/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
from libqtile.command.interface import IPCCommandServer, QtileCommandInterface
from libqtile.config import Click, Drag, Key, KeyChord, Match, Mouse, Rule
from libqtile.config import ScratchPad as ScratchPadConfig
from libqtile.config import Screen
from libqtile.config import Screen, ScreenRect
from libqtile.core.lifecycle import lifecycle
from libqtile.core.loop import LoopContext, QtileEventLoopPolicy
from libqtile.core.state import QtileState
Expand Down Expand Up @@ -357,20 +357,22 @@ def _process_screens(self, reloading: bool = False) -> None:
screens = []

if hasattr(self.config, "fake_screens"):
screen_info = [(s.x, s.y, s.width, s.height) for s in self.config.fake_screens]
screen_info = [
ScreenRect(s.x, s.y, s.width, s.height) for s in self.config.fake_screens
]
config = self.config.fake_screens
else:
# Alias screens with the same x and y coordinates, taking largest
xywh = {} # type: dict[tuple[int, int], tuple[int, int]]
for sx, sy, sw, sh in self.core.get_screen_info():
pos = (sx, sy)
for info in self.core.get_screen_info():
pos = (info.x, info.y)
width, height = xywh.get(pos, (0, 0))
xywh[pos] = (max(width, sw), max(height, sh))
xywh[pos] = (max(width, info.width), max(height, info.height))

screen_info = [(x, y, w, h) for (x, y), (w, h) in xywh.items()]
screen_info = [ScreenRect(x, y, w, h) for (x, y), (w, h) in xywh.items()]
config = self.config.screens

for i, (x, y, w, h) in enumerate(screen_info):
for i, info in enumerate(screen_info):
if i + 1 > len(config):
scr = Screen()
else:
Expand All @@ -392,9 +394,9 @@ def _process_screens(self, reloading: bool = False) -> None:

# If the screen has changed position and/or size, or is a new screen then make sure that any gaps/bars
# are reconfigured
reconfigure_gaps = ((x, y, w, h) != (scr.x, scr.y, scr.width, scr.height)) or (
i + 1 > len(self.screens)
)
reconfigure_gaps = (
(info.x, info.y, info.width, info.height) != (scr.x, scr.y, scr.width, scr.height)
) or (i + 1 > len(self.screens))

if not hasattr(scr, "group"):
# Ensure that this screen actually *has* a group, as it won't get
Expand All @@ -405,7 +407,16 @@ def _process_screens(self, reloading: bool = False) -> None:
# a group anyway.
scr.group = grp

scr._configure(self, i, x, y, w, h, grp, reconfigure_gaps=reconfigure_gaps)
scr._configure(
self,
i,
info.x,
info.y,
info.width,
info.height,
grp,
reconfigure_gaps=reconfigure_gaps,
)
screens.append(scr)

for screen in self.screens:
Expand Down

0 comments on commit ca7b28e

Please sign in to comment.