Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package nwg-displays for openSUSE:Factory checked in at 2024-03-02 23:24:09 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/nwg-displays (Old) and /work/SRC/openSUSE:Factory/.nwg-displays.new.1770 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "nwg-displays" Sat Mar 2 23:24:09 2024 rev:5 rq:1154301 version:0.3.14 Changes: -------- --- /work/SRC/openSUSE:Factory/nwg-displays/nwg-displays.changes 2023-12-11 21:50:25.035230096 +0100 +++ /work/SRC/openSUSE:Factory/.nwg-displays.new.1770/nwg-displays.changes 2024-03-02 23:24:30.069619366 +0100 @@ -1,0 +2,24 @@ +Sat Mar 2 12:15:17 UTC 2024 - Muhammad Akbar Yanuar Mantari <mantari...@pm.me> + +- Fix some rpmlint issues: + + Add fdupes BuildRequires: fix files-duplicate + + fix env-script-interpreter + + fix non-executable-script + +------------------------------------------------------------------- +Sat Mar 2 10:42:02 UTC 2024 - Muhammad Akbar Yanuar Mantari <mantari...@pm.me> + +- Update to version 0.3.14 + + Added XDG_CONFIG_HOME usage for hyprland config; + + Added creation of empty files on: + ~/.config/sway/outputs, + ~/.config/sway/workspaces, + ~/.config/hypr/monitors, + ~/.config/hypr/workspaces, + (or their equivalents in XDG_CONFIG_HOME if not found) + + Documentation updated. + + From now on we rely on the difference between hyprctl monitors + all and hyprctl monitors output to detect disabled monitors. + + The DPMS check button works on Hyprland now. + +------------------------------------------------------------------- Old: ---- nwg-displays-0.3.10.tar.gz New: ---- nwg-displays-0.3.14.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ nwg-displays.spec ++++++ --- /var/tmp/diff_new_pack.TDSTCF/_old 2024-03-02 23:24:30.509635304 +0100 +++ /var/tmp/diff_new_pack.TDSTCF/_new 2024-03-02 23:24:30.513635449 +0100 @@ -1,7 +1,7 @@ # # spec file for package nwg-displays # -# Copyright (c) 2023 SUSE LLC +# Copyright (c) 2024 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,12 +17,13 @@ Name: nwg-displays -Version: 0.3.10 +Version: 0.3.14 Release: 0 Summary: A GTK3 wrapper to display text on the desktop for wlroots License: MIT URL: https://github.com/nwg-piotr/nwg-displays Source0: %{url}/archive/refs/tags/v%{version}.tar.gz#/%{name}-%{version}.tar.gz +BuildRequires: fdupes BuildRequires: gtk-layer-shell-devel BuildRequires: pkgconfig BuildRequires: python3-devel @@ -53,11 +54,23 @@ install -Dpm 0644 nwg-displays.svg -t %{buildroot}%{_datadir}/pixmaps/ install -Dpm 0755 %{name}.desktop -t %{buildroot}%{_datadir}/applications/ +# fix env-script-interpreter +sed -i '1s|#!/usr/bin/env python|#!/usr/bin/python3|' \ + %{buildroot}%{python_sitelib}/nwg_displays/main.py + +# fix non-executable-script +for file in %{buildroot}%{python_sitelib}/nwg_displays/main.py; do + chmod a+x $file +done + +%fdupes %{buildroot}%{python_sitelib}/nwg_displays/__pycache__ + %files %license LICENSE %doc README.md %{_bindir}/%{name} %{_datadir}/applications/*.desktop %{_datadir}/pixmaps/*.svg -%{python_sitelib}/* +%{python_sitelib}/nwg_displays +%{python_sitelib}/nwg_displays-*.egg-info ++++++ nwg-displays-0.3.10.tar.gz -> nwg-displays-0.3.14.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nwg-displays-0.3.10/.github/FUNDING.yml new/nwg-displays-0.3.14/.github/FUNDING.yml --- old/nwg-displays-0.3.10/.github/FUNDING.yml 2023-12-10 13:58:56.000000000 +0100 +++ new/nwg-displays-0.3.14/.github/FUNDING.yml 2024-03-02 02:27:57.000000000 +0100 @@ -1 +1,2 @@ github: nwg-piotr +liberapay: nwg diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nwg-displays-0.3.10/README.md new/nwg-displays-0.3.14/README.md --- old/nwg-displays-0.3.10/README.md 2023-12-10 13:58:56.000000000 +0100 +++ new/nwg-displays-0.3.14/README.md 2024-03-02 02:27:57.000000000 +0100 @@ -1,14 +1,10 @@ -# nwg-displays +<img src="https://github.com/nwg-piotr/nwg-displays/assets/20579136/b7c31822-8846-44be-8028-af3f3af4acd8" width="90" style="margin-right:10px" align=left alt="nwg-shell logo"> +<H1>nwg-displays</H1><br> This application is a part of the [nwg-shell](https://nwg-piotr.github.io/nwg-shell) project. -**Contributing:** please read the [general contributing rules for the nwg-shell project](https://nwg-piotr.github.io/nwg-shell/contribution). - -Output management utility for sway Wayland compositor, inspired by wdisplays and wlay. - -[](https://repology.org/project/nwg-displays/versions) - -**nwg-displays is expected to:** +**Nwg-displays** is an output management utility for [sway](https://github.com/swaywm/sway) and [Hyprland](https://github.com/hyprwm/Hyprland) +Wayland compositor, inspired by wdisplays and wlay. The program is expected to: - provide an intuitive GUI to manage multiple displays; - apply settings; @@ -16,22 +12,28 @@ - save workspace -> output assignments to a text file; - support sway and Hyprland only. - +<img src="https://user-images.githubusercontent.com/20579136/158013748-5b27f742-0e6a-4d82-a5ac-06368b4df008.png" width=640, alt="screenshot"><br> + +## Installation + +Install from your linux distribution repository if possible. + +[](https://repology.org/project/nwg-displays/versions) +Otherwise, clone this repository and run the `install.sh` script. ## Usage ```text -$ nwg-displays -h -usage: nwg-displays [-h] [-g] [-o OUTPUTS_PATH] [-n NUM_WS] [-v] +$ nwg-displays -h +usage: nwg-displays [-h] [-m MONITORS_PATH] [-n NUM_WS] [-v] options: -h, --help show this help message and exit - -g, --generic_names use Generic output names - -o OUTPUTS_PATH, --outputs_path OUTPUTS_PATH - path to save Outputs config to, default: /home/piotr/.config/sway/outputs + -m MONITORS_PATH, --monitors_path MONITORS_PATH + path to save the monitors.conf file to, default: ~/.config/hypr/monitors.conf -n NUM_WS, --num_ws NUM_WS - number of Workspaces in use, default: 8 + number of Workspaces in use, default: 10 -v, --version display version information ``` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nwg-displays-0.3.10/install.sh new/nwg-displays-0.3.14/install.sh --- old/nwg-displays-0.3.10/install.sh 2023-12-10 13:58:56.000000000 +0100 +++ new/nwg-displays-0.3.14/install.sh 2024-03-02 02:27:57.000000000 +0100 @@ -3,3 +3,5 @@ python3 setup.py install --optimize=1 cp nwg-displays.svg /usr/share/pixmaps/ cp nwg-displays.desktop /usr/share/applications/ +install -Dm 644 -t "/usr/share/licenses/nwg-displays" LICENSE +install -Dm 644 -t "/usr/share/doc/nwg-displays" README.md \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nwg-displays-0.3.10/nwg_displays/main.py new/nwg-displays-0.3.14/nwg_displays/main.py --- old/nwg-displays-0.3.10/nwg_displays/main.py 2023-12-10 13:58:56.000000000 +0100 +++ new/nwg-displays-0.3.14/nwg_displays/main.py 2024-03-02 02:27:57.000000000 +0100 @@ -4,7 +4,7 @@ Output management utility for sway Wayland compositor, inspired by wdisplays and wlay Project: https://github.com/nwg-piotr/nwg-displays Author's email: nwg.pi...@gmail.com -Copyright (c) 2022 Piotr Miller +Copyright (c) 2022-2024 Piotr Miller & Contributors License: MIT Depends on: 'python-i3ipc' 'gtk-layer-shell' @@ -46,12 +46,23 @@ sway_config_dir = os.path.join(get_config_home(), "sway") if sway and not os.path.isdir(sway_config_dir): print("WARNING: Couldn't find sway config directory '{}'".format(sway_config_dir), file=sys.stderr) - sway_config_dir = "" + sys.exit(1) -hypr_config_dir = os.path.join(os.getenv("HOME"), ".config/hypr/") +hypr_config_dir = os.path.join(get_config_home(), "hypr") if hypr and not os.path.isdir(hypr_config_dir): print("WARNING: Couldn't find Hyprland config directory '{}'".format(hypr_config_dir), file=sys.stderr) - hypr_config_dir = "" + sys.exit(1) + +# Create empty files if not found +if sway: + for name in ["outputs", "workspaces"]: + create_empty_file(os.path.join(sway_config_dir, name)) +elif hypr: + for name in ["monitors.conf", "workspaces.conf"]: + create_empty_file(os.path.join(hypr_config_dir, name)) +else: + eprint("Neither sway nor Hyprland detected, terminating") + sys.exit(1) config = {} outputs_path = "" @@ -661,7 +672,7 @@ def create_workspaces_window_hypr(btn): global workspaces workspaces = load_workspaces_hypr( - os.path.join(os.getenv("HOME"), ".config", "hypr", "workspaces.conf"), num_ws=num_ws) + os.path.join(hypr_config_dir, "workspaces.conf"), num_ws=num_ws) eprint("WS->Mon:", workspaces) old_workspaces = workspaces.copy() global dialog_win @@ -743,7 +754,8 @@ def on_workspaces_apply_btn_hypr(w, win, old_workspaces): global workspaces if workspaces != old_workspaces: - text_file = open(os.path.join(os.getenv("HOME"), ".config/hypr/workspaces.conf"), "w") + workspace_conf_file = os.path.join(hypr_config_dir, "workspaces.conf") + text_file = open(workspace_conf_file, "w") now = datetime.datetime.now() line = "# Generated by nwg-displays on {} at {}. Do not edit manually.\n".format( @@ -872,11 +884,16 @@ if db.name in outputs_activity and not outputs_activity[db.name]: lines.append("monitor={},disable".format(name)) + cmd = "on" if db.dpms else "off" + hyprctl(f"dispatch dpms {cmd} {db.name}") + print("[Saving]") for line in lines: print(line) - backup = load_text_file(outputs_path).splitlines() + backup = [] + if os.path.isfile(outputs_path): + backup = load_text_file(outputs_path).splitlines() save_list_to_text_file(lines, outputs_path) create_confirm_win(backup, outputs_path) @@ -890,7 +907,7 @@ GtkLayerShell.init_for_window(confirm_win) GtkLayerShell.set_layer(confirm_win, GtkLayerShell.Layer.OVERLAY) - GtkLayerShell.set_keyboard_interactivity(confirm_win, True) + # GtkLayerShell.set_keyboard_mode(confirm_win, GtkLayerShell.KeyboardMode.ON_DEMAND) confirm_win.set_resizable(False) confirm_win.set_modal(True) @@ -999,7 +1016,7 @@ type=str, default="{}/monitors.conf".format(hypr_config_dir), help="path to save the monitors.conf file to, default: {}".format( - "{}/.config/hypr/monitors.conf".format(os.getenv("HOME")))) + "{}/monitors.conf".format(hypr_config_dir))) parser.add_argument("-n", "--num_ws", @@ -1093,11 +1110,13 @@ global form_dpms form_dpms = builder.get_object("dpms") - if sway: - form_dpms.set_tooltip_text(voc["dpms-tooltip"]) - form_dpms.connect("toggled", on_dpms_toggled) - else: - form_dpms.set_sensitive(False) + form_dpms.set_tooltip_text(voc["dpms-tooltip"]) + form_dpms.connect("toggled", on_dpms_toggled) + # if sway: + # form_dpms.set_tooltip_text(voc["dpms-tooltip"]) + # form_dpms.connect("toggled", on_dpms_toggled) + # else: + # form_dpms.set_sensitive(False) global form_adaptive_sync form_adaptive_sync = builder.get_object("adaptive-sync") @@ -1151,7 +1170,7 @@ global form_scale form_scale = builder.get_object("scale") adj = Gtk.Adjustment(lower=0.1, upper=10, step_increment=0.1, page_increment=10, page_size=1) - form_scale.configure(adj, 0.1, 2) + form_scale.configure(adj, 0.1, 6) form_scale.connect("value-changed", on_scale_changed) global form_scale_filter diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nwg-displays-0.3.10/nwg_displays/tools.py new/nwg-displays-0.3.14/nwg_displays/tools.py --- old/nwg-displays-0.3.10/nwg_displays/tools.py 2023-12-10 13:58:56.000000000 +0100 +++ new/nwg-displays-0.3.14/nwg_displays/tools.py 2024-03-02 02:27:57.000000000 +0100 @@ -52,9 +52,8 @@ def list_outputs(): - outputs_dict = {} - if os.getenv("SWAYSOCK"): + outputs_dict = {} eprint("Running on sway") i3 = Connection() tree = i3.get_tree() @@ -87,13 +86,24 @@ outputs_dict[item.name]["monitor"] = None elif os.getenv("HYPRLAND_INSTANCE_SIGNATURE"): + monitors_all = json.loads(hyprctl("j/monitors all")) + monitors = json.loads(hyprctl("j/monitors")) + active = [] + for item in monitors: + active.append(item["name"]) + outputs_dict = {} + for mon in monitors_all: + name = mon["name"] + outputs_dict[name] = {"active": True} if name in active else {"active": False} + eprint("Running on Hyprland") # This will be tricky. The `hyprctl monitors` command returns just a part of the output attributes we need. - # The `wlr-randr` command returns almost everything, but not "focused". We need to use both commands. :/ + # We need `wlr-randr` command to get modes, current mode and focused status. We need to use both commands. :/ # 1. Mirroring is impossible to check in any way. We need to parse back the monitors.conf file, and it sucks. mirrors = {} - monitors_file = os.path.join(os.getenv("HOME"), ".config/hypr/monitors.conf") + hypr_config_dir = os.path.join(get_config_home(), "hypr") + monitors_file = os.path.join(hypr_config_dir, "monitors.conf") if os.path.isfile(monitors_file): lines = load_text_file(monitors_file).splitlines() for line in lines: @@ -111,67 +121,61 @@ for line in lines: if not line.startswith(" "): name = line.split()[0] - description = line.replace(name, "")[2:-4] - # It may happen that the description contains the adapter translations in parens, e.g.: - # LG Electronics LG ULTRAGEAR 1231234F (DP-2 via HDMI)" - description = description.split(" (")[0] - # Just grab the pure description, e.g. "LG Electronics LG ULTRAGEAR 1231234F" - outputs_dict[name] = {"description": description, - "active": False, - "focused": False, - "modes": [], - "scale_filter": None, # unavailable via wlr-randr nor hyprctl - "dpms": None, # unavailable via wlr-randr nor hyprctl - "mirror": "", - "monitor": None, # we'll assign a Gdk monitor here later - # Prevent from crashing in case we couldn't get it w/ wlr-randr nor hyprctl. - # On Hyprland we make no use of it anyway, as there's no way to turn it on/off. - "adaptive_sync_status": False - } + + outputs_dict[name]["modes"] = [] + outputs_dict[name]["scale_filter"] = None # This value does not exist in Hyprland + outputs_dict[name]["dpms"] = None + outputs_dict[name]["mirror"] = "" + outputs_dict[name]["monitor"] = None if name in mirrors: outputs_dict[name]["mirror"] = mirrors[name] - if "Position" in line: - x_y = line.split()[1].split(',') - outputs_dict[name]["x"] = int(x_y[0]) - outputs_dict[name]["y"] = int(x_y[1]) - if line.startswith(" Enabled"): - outputs_dict[name]["active"] = line.split()[1] == "yes" if line.startswith(" "): parts = line.split() - mode = {"width": int(parts[0].split("x")[0]), "height": int(parts[0].split("x")[1]), - "refresh": float(parts[2]) * 1000} - modes = outputs_dict[name]["modes"] - modes.append(mode) - outputs_dict[name]["modes"] = modes - if "current" in line: - w_h = line.split()[0].split('x') - outputs_dict[name]["physical-width"] = int(w_h[0]) - outputs_dict[name]["physical-height"] = int(w_h[1]) - outputs_dict[name]["refresh"] = float(parts[2]) + if len(parts) > 2: + mode = {"width": int(parts[0].split("x")[0]), "height": int(parts[0].split("x")[1]), + "refresh": float(parts[2]) * 1000} + modes = outputs_dict[name]["modes"] + modes.append(mode) + outputs_dict[name]["modes"] = modes + + # We need to detect current mode here, as values from hyprctl are not exactly the same, + # and we'll be unable to preselect current mode in modes combo + if "current" in line: + w_h = line.split()[0].split('x') + outputs_dict[name]["physical-width"] = int(w_h[0]) + outputs_dict[name]["physical-height"] = int(w_h[1]) + outputs_dict[name]["refresh"] = float(parts[2]) # This may or may not work. We'll try to read the value again from hyprctl -j monitors. if name and line.startswith(" Adaptive Sync:"): outputs_dict[name]["adaptive_sync_status"] = line.split()[1] - if "Transform:" in line: - outputs_dict[name]["transform"] = line.split()[1] - if "Scale" in line: - s = float(line.split()[1]) - outputs_dict[name]["scale"] = s - outputs_dict[name]["logical-width"] = outputs_dict[name]["physical-width"] / s - outputs_dict[name]["logical-height"] = outputs_dict[name]["physical-height"] / s - # 3. Read missing/possibly missing values from hyprctl - output = hyprctl("j/monitors") + output = hyprctl("j/monitors all") monitors = json.loads(output) + transforms = {0: "normal", 1: "90", 2: "180", 3: "270", 4: "flipped", 5: "flipped-90", 6: "flipped-180", + 7: "flipped-270"} for m in monitors: - # wlr-rand does not return this value - outputs_dict[m["name"]]["focused"] = m["focused"] == "yes" - # This may be missing from wlr-rand output, see https://github.com/nwg-piotr/nwg-displays/issues/21 + outputs_dict[m["name"]]["focused"] = m["focused"] outputs_dict[m["name"]]["adaptive_sync_status"] = "enabled" if m["vrr"] else "disabled" + outputs_dict[m["name"]]["description"] = f'{m["make"]} {m["model"]} {m["serial"]}' + outputs_dict[m["name"]]["x"] = int(m["x"]) + outputs_dict[m["name"]]["y"] = int(m["y"]) + # outputs_dict[m["name"]]["refresh"] = m["refreshRate"] + outputs_dict[m["name"]]["logical-width"] = m["width"] + outputs_dict[m["name"]]["logical-height"] = m["height"] + # outputs_dict[m["name"]]["physical-width"] = m["width"] / m["scale"] + # outputs_dict[m["name"]]["physical-height"] = m["height"] / m["scale"] + outputs_dict[m["name"]]["transform"] = transforms[m["transform"]] + outputs_dict[m["name"]]["scale"] = m["scale"] + outputs_dict[m["name"]]["focused"] = m["focused"] + outputs_dict[m["name"]]["dpms"] = m["dpmsStatus"] + # to identify Gdk.Monitor + outputs_dict[m["name"]]["model"] = m["model"] + else: eprint("This program only supports sway and Hyprland, and we seem to be elsewhere, terminating.") sys.exit(1) @@ -181,11 +185,19 @@ for i in range(display.get_n_monitors()): monitor = display.get_monitor(i) geometry = monitor.get_geometry() - - for key in outputs_dict: - if int(outputs_dict[key]["x"]) == geometry.x and int(outputs_dict[key]["y"]) == geometry.y: - outputs_dict[key]["monitor"] = monitor - break + if os.getenv("HYPRLAND_INSTANCE_SIGNATURE"): + # This will fail for 2 displays of the same model and coordinates, but we have no better way + for key in outputs_dict: + if (int(outputs_dict[key]["x"]) == geometry.x and int(outputs_dict[key]["y"]) == geometry.y + and outputs_dict[key]["model"] == monitor.get_model()): + outputs_dict[key]["monitor"] = monitor + break + else: + # we don't know the model value on sway :/ + for key in outputs_dict: + if int(outputs_dict[key]["x"]) == geometry.x and int(outputs_dict[key]["y"]) == geometry.y: + outputs_dict[key]["monitor"] = monitor + break for key in outputs_dict: eprint(key, outputs_dict[key]) @@ -201,13 +213,15 @@ result[o.name] = o.active elif os.getenv("HYPRLAND_INSTANCE_SIGNATURE"): - lines = subprocess.check_output("wlr-randr", shell=True).decode("utf-8").strip().splitlines() - name = "" - for line in lines: - if not line.startswith(" "): - name = line.split()[0] - if name and line.startswith(" Enabled"): - result[name] = line.split()[1] == "yes" + monitors_all = json.loads(hyprctl("j/monitors all")) + monitors = json.loads(hyprctl("j/monitors")) + active = [] + for item in monitors: + active.append(item["name"]) + + for mon in monitors_all: + name = mon["name"] + result[name] = True if name in active else False return result @@ -317,6 +331,12 @@ text_file.close() +def create_empty_file(file_path): + if not os.path.isfile(file_path): + with open(file_path, "w") as file: + pass + + def load_text_file(path): try: with open(path, 'r') as file: @@ -379,7 +399,7 @@ except Exception as e: eprint("Error parsing workspaces.conf file: {}".format(e)) - return {}, {} + return {} def save_workspaces(data_dict, path, use_desc=False): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nwg-displays-0.3.10/setup.py new/nwg-displays-0.3.14/setup.py --- old/nwg-displays-0.3.10/setup.py 2023-12-10 13:58:56.000000000 +0100 +++ new/nwg-displays-0.3.14/setup.py 2024-03-02 02:27:57.000000000 +0100 @@ -9,7 +9,7 @@ setup( name='nwg-displays', - version='0.3.10', + version='0.3.14', description='nwg-shell output configuration utility', packages=find_packages(), include_package_data=True,