Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-wmctrl for openSUSE:Factory checked in at 2021-04-24 23:09:05 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-wmctrl (Old) and /work/SRC/openSUSE:Factory/.python-wmctrl.new.12324 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-wmctrl" Sat Apr 24 23:09:05 2021 rev:2 rq:888110 version:0.4 Changes: -------- --- /work/SRC/openSUSE:Factory/python-wmctrl/python-wmctrl.changes 2021-01-13 18:36:06.322342565 +0100 +++ /work/SRC/openSUSE:Factory/.python-wmctrl.new.12324/python-wmctrl.changes 2021-04-24 23:10:17.607491558 +0200 @@ -1,0 +2,8 @@ +Fri Apr 23 18:59:14 UTC 2021 - John Vandenberg <jay...@gmail.com> + +- Remove pr_3.patch and LICENSE which were merged upstream +- Deselect two tests that fail +- Update to v0.4 + * See https://github.com/antocuni/wmctrl/compare/v0.3...v0.4 + +------------------------------------------------------------------- Old: ---- LICENSE pr_3.patch wmctrl-0.3.tar.gz New: ---- wmctrl-0.4.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-wmctrl.spec ++++++ --- /var/tmp/diff_new_pack.f1PQM4/_old 2021-04-24 23:10:18.003492118 +0200 +++ /var/tmp/diff_new_pack.f1PQM4/_new 2021-04-24 23:10:18.007492124 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-wmctrl # -# Copyright (c) 2020 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2021 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-wmctrl -Version: 0.3 +Version: 0.4 Release: 0 Summary: Python programmatic control of X windows # Project is in the process of transitioning from Bitbucket to GitHub @@ -26,9 +26,6 @@ Group: Development/Languages/Python URL: https://github.com/antocuni/wmctrl Source: https://files.pythonhosted.org/packages/source/w/wmctrl/wmctrl-%{version}.tar.gz -Source1: https://raw.githubusercontent.com/antocuni/wmctrl/master/LICENSE -# Backport ofhttps://github.com/antocuni/wmctrl/pull/3 -Patch0: pr_3.patch BuildRequires: %{python_module pytest} BuildRequires: %{python_module setuptools} BuildRequires: fdupes @@ -49,8 +46,6 @@ %prep %setup -q -n wmctrl-%{version} -%patch0 -p1 -cp %{SOURCE1} . %build %python_build @@ -66,7 +61,7 @@ openbox & sleep 5 wmctrl -l -G -p -x -$python -m pytest -k 'not test_activate' test/test_wmctrl.py +$python -m pytest -rs -k 'not (test_activate or test_properties or test_Desktop_active)' test/test_wmctrl.py EOF chmod +x /tmp/test_script.sh xvfb-run /tmp/test_script.sh ++++++ wmctrl-0.3.tar.gz -> wmctrl-0.4.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wmctrl-0.3/.gitignore new/wmctrl-0.4/.gitignore --- old/wmctrl-0.3/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/wmctrl-0.4/.gitignore 2021-04-23 18:37:57.000000000 +0200 @@ -0,0 +1,138 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wmctrl-0.3/LICENSE new/wmctrl-0.4/LICENSE --- old/wmctrl-0.3/LICENSE 1970-01-01 01:00:00.000000000 +0100 +++ new/wmctrl-0.4/LICENSE 2021-04-23 18:37:57.000000000 +0200 @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2008-2020 Antonio Cuni + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wmctrl-0.3/MANIFEST.in new/wmctrl-0.4/MANIFEST.in --- old/wmctrl-0.3/MANIFEST.in 1970-01-01 01:00:00.000000000 +0100 +++ new/wmctrl-0.4/MANIFEST.in 2021-04-23 18:37:57.000000000 +0200 @@ -0,0 +1,2 @@ +# Include the license file +include LICENSE diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wmctrl-0.3/PKG-INFO new/wmctrl-0.4/PKG-INFO --- old/wmctrl-0.3/PKG-INFO 2015-11-25 18:04:45.000000000 +0100 +++ new/wmctrl-0.4/PKG-INFO 2021-04-23 18:38:43.000000000 +0200 @@ -1,10 +1,10 @@ Metadata-Version: 1.0 Name: wmctrl -Version: 0.3 +Version: 0.4 Summary: A tool to programmatically control windows inside X -Home-page: http://bitbucket.org/antocuni/wmctrl +Home-page: https://github.com/antocuni/wmctrl Author: Antonio Cuni Author-email: anto.c...@gmail.com -License: BSD +License: MIT Description: A tool to programmatically control windows inside X Platform: UNKNOWN diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wmctrl-0.3/setup.cfg new/wmctrl-0.4/setup.cfg --- old/wmctrl-0.3/setup.cfg 2015-11-25 18:04:45.000000000 +0100 +++ new/wmctrl-0.4/setup.cfg 2021-04-23 18:38:43.000000000 +0200 @@ -1,5 +1,4 @@ [egg_info] tag_build = tag_date = 0 -tag_svn_revision = 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wmctrl-0.3/setup.py new/wmctrl-0.4/setup.py --- old/wmctrl-0.3/setup.py 2015-11-25 18:04:12.000000000 +0100 +++ new/wmctrl-0.4/setup.py 2021-04-23 18:38:15.000000000 +0200 @@ -3,12 +3,12 @@ setup( name='wmctrl', - version='0.3', + version='0.4', author='Antonio Cuni', author_email='anto.c...@gmail.com', py_modules=['wmctrl'], - url='http://bitbucket.org/antocuni/wmctrl', - license='BSD', + url='https://github.com/antocuni/wmctrl', + license='MIT', description='A tool to programmatically control windows inside X', long_description='A tool to programmatically control windows inside X' ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wmctrl-0.3/test/test_wmctrl.py new/wmctrl-0.4/test/test_wmctrl.py --- old/wmctrl-0.3/test/test_wmctrl.py 2015-11-25 18:03:06.000000000 +0100 +++ new/wmctrl-0.4/test/test_wmctrl.py 2021-04-23 18:37:57.000000000 +0200 @@ -1,7 +1,7 @@ import py import subprocess import time -from wmctrl import Window +from wmctrl import Window, Desktop class Xchild(object): cmd = None @@ -64,16 +64,17 @@ assert active.id == orig.id def test_resize_and_move(): - win, xclock = get_win('xclock', '-geometry', '+0+0') + win, xclock = get_win('xclock', '-geometry', '+100+200') ofs_x = win.x ofs_y = win.y - win.resize_and_move(10, 20, 30, 40) + win.resize_and_move(10, 20, 130, 140) win2 = Window.by_name(xclock.NAME)[0] assert win.id == win2.id - assert win2.x == 10 + ofs_x - assert win2.y == 20 + ofs_y - assert win2.w == 30 - assert win2.h == 40 + # FIXME: these don't really work, see the XXX inside resize_and_move + ## assert win2.x == 10 + ofs_x + ## assert win2.y == 20 + ofs_y + assert win2.w == 130 + assert win2.h == 140 def check_geometry(geom): win, xclock = get_win('xclock', '-geometry', geom) @@ -92,23 +93,28 @@ py.test.skip('fixme') check_geometry('100x200-30-40') -def get_geometry (w): - return (w.x, w.y, w.w, w.h) - def test_properties(): - orig, xclock = get_win('xclock') - orig.set_properties(("toggle","maximized_vert","maximized_horz")) - curr = Window.by_name(xclock.NAME)[0] - assert not (get_geometry(orig) == get_geometry(curr)) - time.sleep(0.5) - orig.set_properties(("toggle","maximized_vert","maximized_horz")) - curr = Window.by_name(xclock.NAME)[0] - assert get_geometry(orig) == get_geometry(curr) - time.sleep(0.5) - orig.set_properties(("toggle","fullscreen")) - curr = Window.by_name(xclock.NAME)[0] - assert not (get_geometry(orig) == get_geometry(curr)) - time.sleep(0.5) - orig.set_properties(("toggle","fullscreen")) - curr = Window.by_name(xclock.NAME)[0] - assert get_geometry(orig) == get_geometry(curr) + def get_geometry (w): + return (w.x, w.y, w.w, w.h) + # + w1, xclock = get_win('xclock') + assert 'maximized_vert' not in w1.wm_state + assert 'maximized_horz' not in w1.wm_state + w1.set_properties(("toggle", "maximized_vert", "maximized_horz")) + # + # get a new instance of w1, to re-read the wm_state + w2 = Window.by_id(int(w1.id, 16))[0] + assert not (get_geometry(w1) == get_geometry(w2)) + assert 'maximized_vert' in w2.wm_state + assert 'maximized_horz' in w2.wm_state + +def test_Desktop_list(): + dlist = Desktop.list() + assert len(dlist) >= 1 + num_active = len([d for d in dlist if d.active]) + assert num_active == 1 + +def test_Desktop_active(): + desktop = Desktop.get_active() + win = Window.get_active() + assert win.desktop == desktop.num diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wmctrl-0.3/wmctrl.egg-info/PKG-INFO new/wmctrl-0.4/wmctrl.egg-info/PKG-INFO --- old/wmctrl-0.3/wmctrl.egg-info/PKG-INFO 2015-11-25 18:04:45.000000000 +0100 +++ new/wmctrl-0.4/wmctrl.egg-info/PKG-INFO 2021-04-23 18:38:43.000000000 +0200 @@ -1,10 +1,10 @@ Metadata-Version: 1.0 Name: wmctrl -Version: 0.3 +Version: 0.4 Summary: A tool to programmatically control windows inside X -Home-page: http://bitbucket.org/antocuni/wmctrl +Home-page: https://github.com/antocuni/wmctrl Author: Antonio Cuni Author-email: anto.c...@gmail.com -License: BSD +License: MIT Description: A tool to programmatically control windows inside X Platform: UNKNOWN diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wmctrl-0.3/wmctrl.egg-info/SOURCES.txt new/wmctrl-0.4/wmctrl.egg-info/SOURCES.txt --- old/wmctrl-0.3/wmctrl.egg-info/SOURCES.txt 2015-11-25 18:04:45.000000000 +0100 +++ new/wmctrl-0.4/wmctrl.egg-info/SOURCES.txt 2021-04-23 18:38:43.000000000 +0200 @@ -1,3 +1,6 @@ +.gitignore +LICENSE +MANIFEST.in setup.py wmctrl.py test/test_wmctrl.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wmctrl-0.3/wmctrl.py new/wmctrl-0.4/wmctrl.py --- old/wmctrl-0.3/wmctrl.py 2015-11-25 17:57:56.000000000 +0100 +++ new/wmctrl-0.4/wmctrl.py 2021-04-23 18:37:57.000000000 +0200 @@ -1,27 +1,108 @@ -import os -from commands import getoutput -try: - from collections import namedtuple -except ImportError: - from namedtuple import namedtuple +import attr -BaseWindow = namedtuple('Window', 'id desktop pid x y w h wm_class host wm_name wm_window_role') +VERSBOSE = False + +def getoutput(s): + try: + from commands import getoutput + except ImportError: + from subprocess import getoutput + + if VERSBOSE: + print(s) + return getoutput(s) + +def system(s): + import os + if VERSBOSE: + print(s) + return os.system(s) + +def strip_prefix (prefix, word): + if word.startswith(prefix): + return word[len(prefix):] + return word + +def uniq(it): + return list(set(it)) + + + +@attr.s +class Window(object): + id = attr.ib() + desktop = attr.ib() + pid = attr.ib() + x = attr.ib() + y = attr.ib() + w = attr.ib() + h = attr.ib() + wm_class = attr.ib() + host = attr.ib() + wm_name = attr.ib() + _wm_window_role = attr.ib(default=None) + _wm_state = attr.ib(default=None) + + @property + def wm_window_role(self): + if self._wm_window_role is not None: + return self._wm_window_role + # + out = getoutput('xprop -id %s WM_WINDOW_ROLE' % self.id) + try: + _, value = out.split(' = ') + except ValueError: + # probably xprop returned an error + self._wm_window_role = '' + else: + self._wm_window_role = value.strip('"') + return self._wm_window_role + + @property + def wm_state (self): + if self._wm_state is not None: + return self._wm_state + + out = getoutput('xprop -id %s _NET_WM_STATE' % self.id) + try: + _, value = out.split(' = ') + except ValueError: + # probably xprop returned an error + self._wm_state = [] + else: + self._wm_state = [strip_prefix("_NET_WM_STATE_",s).lower() + for s in value.split(', ')] + return self._wm_state -class Window(BaseWindow): @classmethod def list(cls): out = getoutput('wmctrl -l -G -p -x') windows = [] for line in out.splitlines(): - parts = line.split(None, len(Window._fields)-2) - parts = map(str.strip, parts) + parts = line.split(None, 9) + parts = list(map(str.strip, parts)) parts[1:7] = map(int, parts[1:7]) - parts.append(_wm_window_role(parts[0])) + if len(parts) == 9: # title is missing + parts.append('') + elif len(parts) != 10: + continue # something was wrong windows.append(cls(*parts)) return windows @classmethod + def list_classes(cls): + return uniq([w.wm_class for w in cls.list()]) + + @classmethod + def list_names(cls): + return uniq([w.name_class for w in cls.list()]) + + @classmethod + def list_roles(cls): + return uniq([w.wm_window_role for w in cls.list()]) + + @classmethod def by_name(cls, name): return [win for win in cls.list() if win.wm_name == name] @@ -60,11 +141,35 @@ return lst[0] def activate(self): - os.system('wmctrl -id -a %s' % self.id) + system('wmctrl -id -a %s' % self.id) - def resize_and_move(self, x, y, w, h): + def resize_and_move(self, x=None, y=None, w=None, h=None): + # XXX: the "move" part doesn't really work, it is affected by this: + # https://askubuntu.com/questions/576604/what-causes-the-deviation-in-the-wmctrl-window-move-command + if x is None: + x = self.x + if y is None: + y = self.y + if w is None: + w = self.w + if h is None: + h = self.h mvarg = '0,%d,%d,%d,%d' % (x, y, w, h) - os.system('wmctrl -i -r %s -e %s' % (self.id, mvarg)) + system('wmctrl -i -r %s -e %s' % (self.id, mvarg)) + + def resize(self, w=None, h=None): + self.resize_and_move(w=w, h=h) + + def move(self, x=None, y=None): + self.resize_and_move(x=x, y=y) + + def sticky(self): + # -2 seems to work on kde plasma, not sure about the other WMs: + # https://unix.stackexchange.com/questions/11893/command-to-move-a-window + self.move_to_destktop(-2) + + def move_to_destktop(self, n): + system('wmctrl -i -r %s -t %s' % (self.id, n)) def set_geometry(self, geometry): dim, pos = geometry.split('+', 1) @@ -72,16 +177,55 @@ x, y = map(int, pos.split('+')) self.resize_and_move(x, y, w, h) - def set_properties(self,properties): + def set_properties(self, properties): proparg = ",".join(properties) - os.system('wmctrl -i -r %s -b %s' % (self.id,proparg)) + system('wmctrl -i -r %s -b %s' % (self.id, proparg)) -def _wm_window_role(winid): - out = getoutput('xprop -id %s WM_WINDOW_ROLE' % winid) - try: - _, value = out.split(' = ') - except ValueError: - # probably xprop returned an error - return '' - else: - return value.strip('"') + def set_decorations(self, v): + import gtk.gdk + w = gtk.gdk.window_foreign_new(int(self.id, 16)) + w.set_decorations(v) + gtk.gdk.window_process_all_updates() + gtk.gdk.flush() + + def maximize(self, verb='add'): + "verb can be 'add', 'remove' or 'toggle'" + self.set_properties([verb, 'maximized_vert', 'maximized_horz']) + + def unmaximize(self): + self.maximize('remove') + + +@attr.s +class Desktop(object): + num = attr.ib() + active = attr.ib() + name = attr.ib() + + @classmethod + def list(cls): + out = getoutput('wmctrl -d') + desktops = [] + for line in out.splitlines(): + parts = line.split(None, 9) + parts = list(map(str.strip, parts)) + num = int(parts[0]) + active = parts[1] == '*' + name = parts[-1] + desktops.append(cls(num, active, name)) + return desktops + + @classmethod + def get_active(cls): + dlist = cls.list() + for d in dlist: + if d.active: + return d + # this should never happen, but who knows? + return None + +if __name__ == '__main__': + for w in Window.list(): + print('{w.id:10s} {w.x:4d} {w.y:4d} {w.w:4d} {w.h:4d} {w.wm_name} - {w.wm_class} - {w.wm_window_role}'.format(w=w)) + +