From 9b37863e4c99f923e7dd2100af41341463ed9d3d Mon Sep 17 00:00:00 2001 From: Dennis Tomas Date: Mon, 30 Mar 2015 19:33:55 +0200 Subject: [PATCH 01/22] Support for multiple screens. --- AppRun | 105 ++++++++++++++++++++++++++++++++++++------------------ xrandr.py | 55 +++++++++++++++++++--------- 2 files changed, 109 insertions(+), 51 deletions(-) diff --git a/AppRun b/AppRun index 662ec42..f9b82b6 100755 --- a/AppRun +++ b/AppRun @@ -34,8 +34,14 @@ class ResBox(rox.Dialog): from rox import filer filer.open_dir(os.path.join(rox.app_dir, 'Help')) elif r == int(g.RESPONSE_OK): - current = self.settings[self.resolutions.get_history()] - xrandr.set_mode(current) + for (i, output), settings in self.settings.iteritems(): + current = settings[self.resolutions[output].get_history()] + enabled = self.enabled[output].get_active() + xrandr.set_mode(current, enabled) + current, self.settings = xrandr.get_settings() + for output, setting in current.iteritems(): + phy_size = '%s x %s' % (setting.phy_width, setting.phy_height) + self.physical_size[output].value.set_text(phy_size) self.hide() self.present() else: @@ -44,48 +50,79 @@ class ResBox(rox.Dialog): sg = g.SizeGroup(g.SIZE_GROUP_BOTH) - vbox = g.VBox(False, 4) - self.vbox.pack_start(vbox, True, True, 0) - vbox.set_border_width(5) + outputs_hbox = g.HBox(False, 4) + self.vbox.pack_start(outputs_hbox, True, True, 0) - hbox = g.HBox(False, 4) - vbox.pack_start(hbox, False, True, 0) - label = g.Label(_('Resolution: ')) - sg.add_widget(label) - label.set_alignment(1, 0.5) - hbox.pack_start(label, False, True, 0) - self.resolutions = g.OptionMenu() - hbox.pack_start(self.resolutions, True, True, 0) - - menu = g.Menu() - self.resolutions.set_menu(menu) current, self.settings = xrandr.get_settings() - for s in self.settings: - item = g.MenuItem(str(s)) - menu.append(item) - item.show() - self.resolutions.connect('changed', self.show_details) - self.refresh_rates = Field(_('Refresh rates: '), sg) - vbox.pack_start(self.refresh_rates, False, True, 0) + self.resolutions = {} + self.refresh_rates = {} + self.physical_size = {} + self.enabled = {} - self.physical_size = Field(_('Physical size: '), sg) - vbox.pack_start(self.physical_size, False, True, 0) + for (i, output), settings in sorted(self.settings.iteritems(), + key=lambda x: x[0][0]): + frame = g.Frame(output) + vbox = g.VBox(False, 4) + vbox.set_border_width(5) + frame.add(vbox) - if current is not None: - i = self.settings.index(current) - else: - i = 0 - self.resolutions.set_history(i) + outputs_hbox.pack_start(frame, False, True, 0) + hbox = g.HBox(False, 4) + vbox.pack_start(hbox, False, True, 0) + label = g.Label(_('Resolution: ')) + sg.add_widget(label) + label.set_alignment(1, 0.5) + hbox.pack_start(label, False, True, 0) + self.resolutions[output] = g.OptionMenu() + hbox.pack_start(self.resolutions[output], True, True, 0) + + menu = g.Menu() + self.resolutions[output].set_menu(menu) + for s in settings: + item = g.MenuItem(str(s)) + menu.append(item) + item.show() + self.resolutions[output].connect('changed', self.show_details, + i, output) + + self.enabled[output] = g.CheckButton(_('Enabled')) + self.enabled[output].set_active( + output in current and current[output].enabled) + self.enabled[output].connect('toggled', self.enabled_toggled, output) + vbox.pack_start(self.enabled[output]) + + self.refresh_rates[output] = Field(_('Refresh rates: '), sg) + vbox.pack_start(self.refresh_rates[output], False, True, 0) + + self.physical_size[output] = Field(_('Physical size: '), sg) + vbox.pack_start(self.physical_size[output], False, True, 0) + + try: + i = settings.index(current[output]) + except KeyError: + i = 0 + self.resolutions[output].set_history(i) self.vbox.show_all() - def show_details(self, resolutions): - current = self.settings[resolutions.get_history()] + def enabled_toggled(self, checkbutton, output): + enabled_checkbuttons = [ + button for button in self.enabled.itervalues() + if button.get_active() + ] + if len(enabled_checkbuttons) == 1: + enabled_checkbuttons[0].set_sensitive(False) + else: + for checkbutton in self.enabled.itervalues(): + checkbutton.set_sensitive(True) + + def show_details(self, resolutions, i, output): + current = self.settings[i, output][resolutions.get_history()] rates = ', '.join(['%d Hz' % r for r in current.res]) phy_size = '%s x %s' % (current.phy_width, current.phy_height) - self.physical_size.value.set_text(phy_size) - self.refresh_rates.value.set_text(rates) + self.physical_size[output].value.set_text(phy_size) + self.refresh_rates[output].value.set_text(rates) try: diff --git a/xrandr.py b/xrandr.py index 17a8daa..342e7e4 100644 --- a/xrandr.py +++ b/xrandr.py @@ -1,4 +1,5 @@ import os, popen2, sys +from collections import defaultdict import rox paths = os.environ.get('PATH', '/bin:/usr/bin').split(':') @@ -15,13 +16,15 @@ 'system. Try upgrading your X server.')) class Setting: - def __init__(self, line): + def __init__(self, output, line): bits = [b for b in line.split() if b] + self.output = output self.n = int(bits[0]) self.width = int(bits[1]) self.height = int(bits[3]) self.phy_width = bits[5] self.phy_height = bits[7] + self.enabled = self.phy_width != 0 self.res = [] self.current_r = None for r in bits[9:]: @@ -35,18 +38,23 @@ def __str__(self): return '%s x %s' % (self.width, self.height) class NewSetting(Setting): - def __init__(self, line, phy_width, phy_height): + def __init__(self, output, line, phy_width, phy_height): bits = [b for b in line.split() if b] self.n = -1 + self.output = output self.width, self.height = bits[0].split('x') self.width = int(self.width) self.height = int(self.height) self.phy_width = phy_width self.phy_height = phy_height + self.enabled = self.phy_width != 0 self.res = [] self.current_r = None for r in bits[1:]: - res = float(r.strip('*+')) + try: + res = float(r.strip('*+')) + except ValueError: + continue if r.endswith('*'): self.current_r = res self.res.append(res) @@ -54,35 +62,48 @@ def __init__(self, line, phy_width, phy_height): def get_settings(): cout, cin = popen2.popen2([xrandr]) cin.close() - settings = [] - current = None + settings = defaultdict(list) + current = {} phy_width = 0 phy_height = 0 + output = '' + i = 0 for line in cout: line = line.rstrip() - if 'mm x ' in line and line.endswith('mm'): - data = line.rsplit(' ', 3) - if data[1].endswith('mm') and data[3].endswith('mm'): - phy_width = data[1] - phy_height = data[3] + if 'connected' in line: + if 'disconnected' in line: + continue + phy_width = 0 + phy_height = 0 + if 'mm x ' in line and line.endswith('mm'): + data = line.rsplit(' ', 3) + if data[1].endswith('mm') and data[3].endswith('mm'): + phy_width = data[1] + phy_height = data[3] + output = line.split(' ')[0] + i += 1 elif line[0] in ' *' and 'x' in line: try: if ' x ' in line: - setting = Setting(line[1:]) + setting = Setting(output, line[1:]) else: - setting = NewSetting(line, phy_width, phy_height) - settings.append(setting) + setting = NewSetting(output, line, phy_width, phy_height) + settings[i, output].append(setting) if '*' in line: - current = setting + current[output] = setting except Exception, ex: print >>sys.stderr, "Failed to parse line '%s':\n%s" % \ (line.strip(), str(ex)) cout.close() return (current, settings) -def set_mode(setting): - cerr, cin = popen2.popen4([xrandr, '-s', '%dx%d' % - (setting.width, setting.height)]) +def set_mode(setting, enabled): + cmd = [xrandr, '--output', setting.output] + if enabled: + cmd += ['--mode', '%dx%d' % (setting.width, setting.height)] + else: + cmd.append('--off') + cerr, cin = popen2.popen4(cmd) cin.close() errors = cerr.read() if errors: From 4d2516f23874617c4db4c88736e3c95f463bfe42 Mon Sep 17 00:00:00 2001 From: Dennis Tomas Date: Mon, 30 Mar 2015 20:47:40 +0200 Subject: [PATCH 02/22] Save settings. --- AppRun | 24 ++++++++++++++++-------- xrandr.py | 20 +++++++++++++------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/AppRun b/AppRun index f9b82b6..0d45a94 100755 --- a/AppRun +++ b/AppRun @@ -3,10 +3,14 @@ import findrox; findrox.version(1, 9, 13) import os import rox +from rox.settings import Setting, Settings g = rox.g __builtins__._ = rox.i18n.translation(os.path.join(rox.app_dir, 'Messages')) +settings = Settings() +xrandr_setting = Setting('ROX/XRandRArgs', '', settings) + class Field(g.HBox): def __init__(self, name, sg): g.HBox.__init__(self, False, 4) @@ -34,10 +38,13 @@ class ResBox(rox.Dialog): from rox import filer filer.open_dir(os.path.join(rox.app_dir, 'Help')) elif r == int(g.RESPONSE_OK): - for (i, output), settings in self.settings.iteritems(): - current = settings[self.resolutions[output].get_history()] - enabled = self.enabled[output].get_active() - xrandr.set_mode(current, enabled) + current_settings = [ + settings[self.resolutions[output].get_history()] + for (i, output), settings in self.settings.iteritems() + ] + xrandr.set_modes(current_settings) + xrandr_setting._set(' '.join( + xrandr.settings_to_args(current_settings))) current, self.settings = xrandr.get_settings() for output, setting in current.iteritems(): phy_size = '%s x %s' % (setting.phy_width, setting.phy_height) @@ -83,13 +90,12 @@ class ResBox(rox.Dialog): item = g.MenuItem(str(s)) menu.append(item) item.show() - self.resolutions[output].connect('changed', self.show_details, - i, output) + self.resolutions[output].connect('changed', self.show_details, i, output) self.enabled[output] = g.CheckButton(_('Enabled')) self.enabled[output].set_active( output in current and current[output].enabled) - self.enabled[output].connect('toggled', self.enabled_toggled, output) + self.enabled[output].connect('toggled', self.enabled_toggled, i, output) vbox.pack_start(self.enabled[output]) self.refresh_rates[output] = Field(_('Refresh rates: '), sg) @@ -106,7 +112,9 @@ class ResBox(rox.Dialog): self.vbox.show_all() - def enabled_toggled(self, checkbutton, output): + def enabled_toggled(self, checkbutton, i, output): + for setting in self.settings[i, output]: + setting.enabled = checkbutton.get_active() enabled_checkbuttons = [ button for button in self.enabled.itervalues() if button.get_active() diff --git a/xrandr.py b/xrandr.py index 342e7e4..b972823 100644 --- a/xrandr.py +++ b/xrandr.py @@ -88,7 +88,7 @@ def get_settings(): setting = Setting(output, line[1:]) else: setting = NewSetting(output, line, phy_width, phy_height) - settings[i, output].append(setting) + settings[(i, output)].append(setting) if '*' in line: current[output] = setting except Exception, ex: @@ -97,12 +97,18 @@ def get_settings(): cout.close() return (current, settings) -def set_mode(setting, enabled): - cmd = [xrandr, '--output', setting.output] - if enabled: - cmd += ['--mode', '%dx%d' % (setting.width, setting.height)] - else: - cmd.append('--off') +def settings_to_args(settings): + args = [] + for setting in settings: + args += ['--output', setting.output] + if setting.enabled: + args += ['--mode', '%dx%d' % (setting.width, setting.height)] + else: + args.append('--off') + return args + +def set_modes(settings): + cmd = [xrandr] + settings_to_args(settings) cerr, cin = popen2.popen4(cmd) cin.close() errors = cerr.read() From 56529409d677b6114e538cc94cba216d3634bde1 Mon Sep 17 00:00:00 2001 From: Dennis Tomas Date: Mon, 30 Mar 2015 22:00:49 +0200 Subject: [PATCH 03/22] Select refresh rate. --- AppRun | 25 +++++++++++++++++++++---- xrandr.py | 2 ++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/AppRun b/AppRun index 0d45a94..72fdf9d 100755 --- a/AppRun +++ b/AppRun @@ -98,8 +98,13 @@ class ResBox(rox.Dialog): self.enabled[output].connect('toggled', self.enabled_toggled, i, output) vbox.pack_start(self.enabled[output]) - self.refresh_rates[output] = Field(_('Refresh rates: '), sg) - vbox.pack_start(self.refresh_rates[output], False, True, 0) + self.refresh_rates[output] = g.OptionMenu() + vbox.pack_start(self.refresh_rates[output]) + self.refresh_rates[output].connect('changed', + self.refresh_rate_changed, i, output) + + menu = g.Menu() + self.refresh_rates[output].set_menu(menu) self.physical_size[output] = Field(_('Physical size: '), sg) vbox.pack_start(self.physical_size[output], False, True, 0) @@ -112,6 +117,10 @@ class ResBox(rox.Dialog): self.vbox.show_all() + def refresh_rate_changed(self, refresh_rates, i, output): + current = self.settings[i, output][self.resolutions[output].get_history()] + current.current_r = current.res[refresh_rates.get_history()] + def enabled_toggled(self, checkbutton, i, output): for setting in self.settings[i, output]: setting.enabled = checkbutton.get_active() @@ -127,10 +136,18 @@ class ResBox(rox.Dialog): def show_details(self, resolutions, i, output): current = self.settings[i, output][resolutions.get_history()] - rates = ', '.join(['%d Hz' % r for r in current.res]) phy_size = '%s x %s' % (current.phy_width, current.phy_height) self.physical_size[output].value.set_text(phy_size) - self.refresh_rates[output].value.set_text(rates) + menu = g.Menu() + self.refresh_rates[output].set_menu(menu) + for refresh_rate in current.res: + item = g.MenuItem('%s Hz' % refresh_rate) + menu.append(item) + item.show() + self.refresh_rates[output].set_history( + current.res.index(current.current_r) + if current.current_r is not None else 0 + ) try: diff --git a/xrandr.py b/xrandr.py index b972823..b0ce23a 100644 --- a/xrandr.py +++ b/xrandr.py @@ -105,6 +105,8 @@ def settings_to_args(settings): args += ['--mode', '%dx%d' % (setting.width, setting.height)] else: args.append('--off') + if setting.current_r is not None: + args += ['--rate', str(setting.current_r)] return args def set_modes(settings): From 9ab9d6768de6434178cb952556ab52a7b80ecc08 Mon Sep 17 00:00:00 2001 From: Dennis Tomas Date: Mon, 30 Mar 2015 22:03:11 +0200 Subject: [PATCH 04/22] Fix Enabled sensitivity on startup. --- AppRun | 1 + 1 file changed, 1 insertion(+) diff --git a/AppRun b/AppRun index 72fdf9d..24711b1 100755 --- a/AppRun +++ b/AppRun @@ -97,6 +97,7 @@ class ResBox(rox.Dialog): output in current and current[output].enabled) self.enabled[output].connect('toggled', self.enabled_toggled, i, output) vbox.pack_start(self.enabled[output]) + self.enabled_toggled(self.enabled[output], i, output) self.refresh_rates[output] = g.OptionMenu() vbox.pack_start(self.refresh_rates[output]) From 9a0b0a85e210145809806c2a7ff4012e0ce66e67 Mon Sep 17 00:00:00 2001 From: Dennis Tomas Date: Mon, 30 Mar 2015 22:06:50 +0200 Subject: [PATCH 05/22] Added Refresh Rate label, moved Enabled checkbox to top. --- AppRun | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/AppRun b/AppRun index 24711b1..17fe6ea 100755 --- a/AppRun +++ b/AppRun @@ -74,6 +74,13 @@ class ResBox(rox.Dialog): vbox.set_border_width(5) frame.add(vbox) + self.enabled[output] = g.CheckButton(_('Enabled')) + self.enabled[output].set_active( + output in current and current[output].enabled) + self.enabled[output].connect('toggled', self.enabled_toggled, i, output) + vbox.pack_start(self.enabled[output]) + self.enabled_toggled(self.enabled[output], i, output) + outputs_hbox.pack_start(frame, False, True, 0) hbox = g.HBox(False, 4) vbox.pack_start(hbox, False, True, 0) @@ -92,15 +99,14 @@ class ResBox(rox.Dialog): item.show() self.resolutions[output].connect('changed', self.show_details, i, output) - self.enabled[output] = g.CheckButton(_('Enabled')) - self.enabled[output].set_active( - output in current and current[output].enabled) - self.enabled[output].connect('toggled', self.enabled_toggled, i, output) - vbox.pack_start(self.enabled[output]) - self.enabled_toggled(self.enabled[output], i, output) - + hbox = g.HBox(False, 4) + vbox.pack_start(hbox, False, True, 0) + label = g.Label(_('Refresh Rate: ')) + sg.add_widget(label) + label.set_alignment(1, 0.5) + hbox.pack_start(label, False, True, 0) self.refresh_rates[output] = g.OptionMenu() - vbox.pack_start(self.refresh_rates[output]) + hbox.pack_start(self.refresh_rates[output]) self.refresh_rates[output].connect('changed', self.refresh_rate_changed, i, output) From f746c2d90bcf8fc4066975ea5961111858e6181a Mon Sep 17 00:00:00 2001 From: Dennis Tomas Date: Mon, 30 Mar 2015 22:08:23 +0200 Subject: [PATCH 06/22] Do not show Enabled checkbox if only one screen is found. --- AppRun | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/AppRun b/AppRun index 17fe6ea..4c68475 100755 --- a/AppRun +++ b/AppRun @@ -74,12 +74,12 @@ class ResBox(rox.Dialog): vbox.set_border_width(5) frame.add(vbox) - self.enabled[output] = g.CheckButton(_('Enabled')) - self.enabled[output].set_active( - output in current and current[output].enabled) - self.enabled[output].connect('toggled', self.enabled_toggled, i, output) - vbox.pack_start(self.enabled[output]) - self.enabled_toggled(self.enabled[output], i, output) + if len(self.settings) > 1: + self.enabled[output] = g.CheckButton(_('Enabled')) + self.enabled[output].set_active( + output in current and current[output].enabled) + self.enabled[output].connect('toggled', self.enabled_toggled, i, output) + vbox.pack_start(self.enabled[output]) outputs_hbox.pack_start(frame, False, True, 0) hbox = g.HBox(False, 4) From 3f4559aaeb5baa4dd7bf561c03c266071445b497 Mon Sep 17 00:00:00 2001 From: Dennis Tomas Date: Mon, 30 Mar 2015 22:12:46 +0200 Subject: [PATCH 07/22] Refresh button. --- AppRun | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/AppRun b/AppRun index 4c68475..6eee458 100755 --- a/AppRun +++ b/AppRun @@ -25,11 +25,14 @@ class Field(g.HBox): frame.add(self.value) self.value.set_padding(2, 2) +RESPONSE_REFRESH = 1000 + class ResBox(rox.Dialog): def __init__(self): rox.Dialog.__init__(self, _("Screen Resolution")) self.add_button(g.STOCK_HELP, g.RESPONSE_HELP) self.add_button(g.STOCK_CLOSE, g.RESPONSE_CANCEL) + self.add_button(g.STOCK_REFRESH, RESPONSE_REFRESH) self.add_button(g.STOCK_APPLY, g.RESPONSE_OK) self.set_default_response(g.RESPONSE_OK) self.set_has_separator(False) @@ -51,6 +54,9 @@ class ResBox(rox.Dialog): self.physical_size[output].value.set_text(phy_size) self.hide() self.present() + elif r == RESPONSE_REFRESH: + self.destroy() + ResBox().show() else: d.destroy() self.connect('response', resp) From 6480fb1e2d5687966b023016ceb1113f1ac17716 Mon Sep 17 00:00:00 2001 From: Dennis Tomas Date: Tue, 31 Mar 2015 07:56:31 +0200 Subject: [PATCH 08/22] Screen placement. --- AppRun | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++---- xrandr.py | 9 ++++++- 2 files changed, 75 insertions(+), 5 deletions(-) diff --git a/AppRun b/AppRun index 6eee458..cb43481 100755 --- a/AppRun +++ b/AppRun @@ -27,6 +27,9 @@ class Field(g.HBox): RESPONSE_REFRESH = 1000 +other_direction = {0: 0, 1: 2, 2: 1, 3: 4, 4: 3} +direction_strings = {0: 'same-as', 1: 'left-of', 2: 'right-of', 3: 'above', 4: 'below'} + class ResBox(rox.Dialog): def __init__(self): rox.Dialog.__init__(self, _("Screen Resolution")) @@ -52,6 +55,16 @@ class ResBox(rox.Dialog): for output, setting in current.iteritems(): phy_size = '%s x %s' % (setting.phy_width, setting.phy_height) self.physical_size[output].value.set_text(phy_size) + for (i, output), settings in self.settings.iteritems(): + if i == 0: + continue + for other_output in self.other_output_strings[output]: + for setting in settings: + if setting.other_output is None: + setting.other_output = other_output + direction = self.directions[output].get_history() + for setting in settings: + setting.direction = direction_strings[direction] self.hide() self.present() elif r == RESPONSE_REFRESH: @@ -72,8 +85,11 @@ class ResBox(rox.Dialog): self.refresh_rates = {} self.physical_size = {} self.enabled = {} + self.directions = {} + self.other_outputs = {} + self.other_output_strings = {} - for (i, output), settings in sorted(self.settings.iteritems(), + for (output_index, output), settings in sorted(self.settings.iteritems(), key=lambda x: x[0][0]): frame = g.Frame(output) vbox = g.VBox(False, 4) @@ -84,7 +100,8 @@ class ResBox(rox.Dialog): self.enabled[output] = g.CheckButton(_('Enabled')) self.enabled[output].set_active( output in current and current[output].enabled) - self.enabled[output].connect('toggled', self.enabled_toggled, i, output) + self.enabled[output].connect('toggled', self.enabled_toggled, + output_index, output) vbox.pack_start(self.enabled[output]) outputs_hbox.pack_start(frame, False, True, 0) @@ -103,7 +120,7 @@ class ResBox(rox.Dialog): item = g.MenuItem(str(s)) menu.append(item) item.show() - self.resolutions[output].connect('changed', self.show_details, i, output) + self.resolutions[output].connect('changed', self.show_details, output_index, output) hbox = g.HBox(False, 4) vbox.pack_start(hbox, False, True, 0) @@ -114,7 +131,7 @@ class ResBox(rox.Dialog): self.refresh_rates[output] = g.OptionMenu() hbox.pack_start(self.refresh_rates[output]) self.refresh_rates[output].connect('changed', - self.refresh_rate_changed, i, output) + self.refresh_rate_changed, output_index, output) menu = g.Menu() self.refresh_rates[output].set_menu(menu) @@ -128,8 +145,54 @@ class ResBox(rox.Dialog): i = 0 self.resolutions[output].set_history(i) + if output_index > 0: + hbox = g.HBox(False, 4) + vbox.pack_start(hbox, False, True, 0) + self.directions[output] = g.OptionMenu() + sg.add_widget(self.directions[output]) + hbox.pack_start(self.directions[output], False, True, 0) + menu = g.Menu() + self.directions[output].set_menu(menu) + menu.append(g.MenuItem(_('Same as'))) + menu.append(g.MenuItem(_('Left of'))) + menu.append(g.MenuItem(_('Right of'))) + menu.append(g.MenuItem(_('Above'))) + menu.append(g.MenuItem(_('Below'))) + self.directions[output].set_history(0) + self.directions[output].connect("changed", self.direction_changed, + output_index, output) + + self.other_outputs[output] = g.OptionMenu() + hbox.pack_start(self.other_outputs[output]) + menu = g.Menu() + self.other_outputs[output].set_menu(menu) + self.other_output_strings[output] = [ + other_output for __, other_output in self.settings if other_output != output + ] + for other_output in self.other_output_strings[output]: + item = g.MenuItem(other_output) + menu.append(item) + for setting in settings: + if setting.other_output is None: + setting.other_output = other_output + self.other_outputs[output].set_history(0) + self.other_outputs[output].connect("changed", self.other_output_selected, + output_index, output) + self.vbox.show_all() + def direction_changed(self, directions, i, output): + direction = directions.get_history() + print(direction) + for setting in self.settings[i, output]: + setting.direction = direction_strings[direction] + + def other_output_selected(self, other_outputs, i, output): + other_output = self.other_output_strings[output][other_outputs.get_history()] + print(other_output) + for setting in self.settings[i, output]: + setting.other_output = other_output + def refresh_rate_changed(self, refresh_rates, i, output): current = self.settings[i, output][self.resolutions[output].get_history()] current.current_r = current.res[refresh_rates.get_history()] diff --git a/xrandr.py b/xrandr.py index b0ce23a..5620b4e 100644 --- a/xrandr.py +++ b/xrandr.py @@ -26,6 +26,8 @@ def __init__(self, output, line): self.phy_height = bits[7] self.enabled = self.phy_width != 0 self.res = [] + self.other_output = None + self.direction = 'same-as' self.current_r = None for r in bits[9:]: if r.startswith('*'): @@ -49,6 +51,8 @@ def __init__(self, output, line, phy_width, phy_height): self.phy_height = phy_height self.enabled = self.phy_width != 0 self.res = [] + self.other_output = None + self.direction = 'same-as' self.current_r = None for r in bits[1:]: try: @@ -67,7 +71,7 @@ def get_settings(): phy_width = 0 phy_height = 0 output = '' - i = 0 + i = -1 for line in cout: line = line.rstrip() if 'connected' in line: @@ -107,6 +111,9 @@ def settings_to_args(settings): args.append('--off') if setting.current_r is not None: args += ['--rate', str(setting.current_r)] + if setting.other_output is not None: + args += ['--%s' % setting.direction, setting.other_output] + return args def set_modes(settings): From ab1efe82d1f21e36730a254cc9097aa5d84f5ff8 Mon Sep 17 00:00:00 2001 From: Dennis Tomas Date: Tue, 31 Mar 2015 11:51:24 +0200 Subject: [PATCH 09/22] Read layout from xrandr args setting. --- AppRun | 111 ++++++++++++++++++++++------------------------------ xrandr.py | 114 +++++++++++++++++++++++++++--------------------------- 2 files changed, 104 insertions(+), 121 deletions(-) diff --git a/AppRun b/AppRun index cb43481..4d22948 100755 --- a/AppRun +++ b/AppRun @@ -28,7 +28,10 @@ class Field(g.HBox): RESPONSE_REFRESH = 1000 other_direction = {0: 0, 1: 2, 2: 1, 3: 4, 4: 3} -direction_strings = {0: 'same-as', 1: 'left-of', 2: 'right-of', 3: 'above', 4: 'below'} +direction_strings = { + 0: 'same-as', 1: 'left-of', 2: 'right-of', 3: 'above', 4: 'below', + 'same-as': 0, 'left-of': 1, 'right-of': 2, 'above': 3, 'below': 4 +} class ResBox(rox.Dialog): def __init__(self): @@ -44,29 +47,10 @@ class ResBox(rox.Dialog): from rox import filer filer.open_dir(os.path.join(rox.app_dir, 'Help')) elif r == int(g.RESPONSE_OK): - current_settings = [ - settings[self.resolutions[output].get_history()] - for (i, output), settings in self.settings.iteritems() - ] - xrandr.set_modes(current_settings) - xrandr_setting._set(' '.join( - xrandr.settings_to_args(current_settings))) - current, self.settings = xrandr.get_settings() - for output, setting in current.iteritems(): - phy_size = '%s x %s' % (setting.phy_width, setting.phy_height) - self.physical_size[output].value.set_text(phy_size) - for (i, output), settings in self.settings.iteritems(): - if i == 0: - continue - for other_output in self.other_output_strings[output]: - for setting in settings: - if setting.other_output is None: - setting.other_output = other_output - direction = self.directions[output].get_history() - for setting in settings: - setting.direction = direction_strings[direction] - self.hide() - self.present() + xrandr.set_modes(self.settings) + xrandr_setting._set(' '.join(xrandr.settings_to_args(self.settings))) + self.destroy() + ResBox().show() elif r == RESPONSE_REFRESH: self.destroy() ResBox().show() @@ -79,7 +63,7 @@ class ResBox(rox.Dialog): outputs_hbox = g.HBox(False, 4) self.vbox.pack_start(outputs_hbox, True, True, 0) - current, self.settings = xrandr.get_settings() + self.settings = xrandr.get_settings(xrandr_setting.value) self.resolutions = {} self.refresh_rates = {} @@ -89,8 +73,8 @@ class ResBox(rox.Dialog): self.other_outputs = {} self.other_output_strings = {} - for (output_index, output), settings in sorted(self.settings.iteritems(), - key=lambda x: x[0][0]): + for output, settings in sorted(self.settings.iteritems(), + key=lambda x: x[1].output_index): frame = g.Frame(output) vbox = g.VBox(False, 4) vbox.set_border_width(5) @@ -98,10 +82,12 @@ class ResBox(rox.Dialog): if len(self.settings) > 1: self.enabled[output] = g.CheckButton(_('Enabled')) - self.enabled[output].set_active( - output in current and current[output].enabled) - self.enabled[output].connect('toggled', self.enabled_toggled, - output_index, output) + self.enabled[output].set_active(settings.enabled) + enabled_settings = [ + s for s in self.settings.itervalues() if s.enabled + ] + self.enabled[output].set_sensitive(not settings.enabled or len(enabled_settings) > 1) + self.enabled[output].connect('toggled', self.enabled_toggled, settings) vbox.pack_start(self.enabled[output]) outputs_hbox.pack_start(frame, False, True, 0) @@ -120,7 +106,7 @@ class ResBox(rox.Dialog): item = g.MenuItem(str(s)) menu.append(item) item.show() - self.resolutions[output].connect('changed', self.show_details, output_index, output) + self.resolutions[output].connect('changed', self.show_details, settings) hbox = g.HBox(False, 4) vbox.pack_start(hbox, False, True, 0) @@ -131,7 +117,7 @@ class ResBox(rox.Dialog): self.refresh_rates[output] = g.OptionMenu() hbox.pack_start(self.refresh_rates[output]) self.refresh_rates[output].connect('changed', - self.refresh_rate_changed, output_index, output) + self.refresh_rate_changed, settings) menu = g.Menu() self.refresh_rates[output].set_menu(menu) @@ -140,12 +126,12 @@ class ResBox(rox.Dialog): vbox.pack_start(self.physical_size[output], False, True, 0) try: - i = settings.index(current[output]) - except KeyError: + i = settings.index(settings.current) + except (ValueError, KeyError): i = 0 self.resolutions[output].set_history(i) - if output_index > 0: + if settings.output_index > 0: hbox = g.HBox(False, 4) vbox.pack_start(hbox, False, True, 0) self.directions[output] = g.OptionMenu() @@ -158,48 +144,43 @@ class ResBox(rox.Dialog): menu.append(g.MenuItem(_('Right of'))) menu.append(g.MenuItem(_('Above'))) menu.append(g.MenuItem(_('Below'))) - self.directions[output].set_history(0) + self.directions[output].set_history( + direction_strings.get(settings.direction, 0)) self.directions[output].connect("changed", self.direction_changed, - output_index, output) + settings) self.other_outputs[output] = g.OptionMenu() hbox.pack_start(self.other_outputs[output]) menu = g.Menu() self.other_outputs[output].set_menu(menu) self.other_output_strings[output] = [ - other_output for __, other_output in self.settings if other_output != output + other_output for other_output in self.settings if other_output != output ] for other_output in self.other_output_strings[output]: item = g.MenuItem(other_output) menu.append(item) - for setting in settings: - if setting.other_output is None: - setting.other_output = other_output + if settings.other_output is None: + settings.other_output = other_output self.other_outputs[output].set_history(0) self.other_outputs[output].connect("changed", self.other_output_selected, - output_index, output) + settings) self.vbox.show_all() - def direction_changed(self, directions, i, output): + def direction_changed(self, directions, settings): direction = directions.get_history() - print(direction) - for setting in self.settings[i, output]: - setting.direction = direction_strings[direction] - - def other_output_selected(self, other_outputs, i, output): - other_output = self.other_output_strings[output][other_outputs.get_history()] - print(other_output) - for setting in self.settings[i, output]: - setting.other_output = other_output - - def refresh_rate_changed(self, refresh_rates, i, output): - current = self.settings[i, output][self.resolutions[output].get_history()] + settings.direction = direction_strings[direction] + + def other_output_selected(self, other_outputs, settings): + other_output = self.other_output_strings[settings.output][other_outputs.get_history()] + settings.other_output = other_output + + def refresh_rate_changed(self, refresh_rates, settings): + current = settings.current current.current_r = current.res[refresh_rates.get_history()] - def enabled_toggled(self, checkbutton, i, output): - for setting in self.settings[i, output]: - setting.enabled = checkbutton.get_active() + def enabled_toggled(self, checkbutton, settings): + settings.enabled = checkbutton.get_active() enabled_checkbuttons = [ button for button in self.enabled.itervalues() if button.get_active() @@ -210,17 +191,17 @@ class ResBox(rox.Dialog): for checkbutton in self.enabled.itervalues(): checkbutton.set_sensitive(True) - def show_details(self, resolutions, i, output): - current = self.settings[i, output][resolutions.get_history()] - phy_size = '%s x %s' % (current.phy_width, current.phy_height) - self.physical_size[output].value.set_text(phy_size) + def show_details(self, resolutions, settings): + settings.current = current = settings[resolutions.get_history()] + phy_size = '%s x %s' % (settings.phy_width, settings.phy_height) + self.physical_size[settings.output].value.set_text(phy_size) menu = g.Menu() - self.refresh_rates[output].set_menu(menu) + self.refresh_rates[settings.output].set_menu(menu) for refresh_rate in current.res: item = g.MenuItem('%s Hz' % refresh_rate) menu.append(item) item.show() - self.refresh_rates[output].set_history( + self.refresh_rates[settings.output].set_history( current.res.index(current.current_r) if current.current_r is not None else 0 ) diff --git a/xrandr.py b/xrandr.py index 5620b4e..fa5d056 100644 --- a/xrandr.py +++ b/xrandr.py @@ -16,43 +16,13 @@ 'system. Try upgrading your X server.')) class Setting: - def __init__(self, output, line): - bits = [b for b in line.split() if b] - self.output = output - self.n = int(bits[0]) - self.width = int(bits[1]) - self.height = int(bits[3]) - self.phy_width = bits[5] - self.phy_height = bits[7] - self.enabled = self.phy_width != 0 - self.res = [] - self.other_output = None - self.direction = 'same-as' - self.current_r = None - for r in bits[9:]: - if r.startswith('*'): - self.current_r = int(r[1:]) - self.res.append(self.current_r) - else: - self.res.append(int(r)) - - def __str__(self): - return '%s x %s' % (self.width, self.height) - -class NewSetting(Setting): - def __init__(self, output, line, phy_width, phy_height): + def __init__(self, line): bits = [b for b in line.split() if b] self.n = -1 - self.output = output self.width, self.height = bits[0].split('x') self.width = int(self.width) self.height = int(self.height) - self.phy_width = phy_width - self.phy_height = phy_height - self.enabled = self.phy_width != 0 self.res = [] - self.other_output = None - self.direction = 'same-as' self.current_r = None for r in bits[1:]: try: @@ -63,15 +33,27 @@ def __init__(self, output, line, phy_width, phy_height): self.current_r = res self.res.append(res) -def get_settings(): + def __str__(self): + return '%s x %s' % (self.width, self.height) + +class Settings(list): + + def __init__(self, output_index, output, phy_width, phy_height): + self.output = output + self.output_index = output_index + self.phy_width = phy_width + self.phy_height = phy_height + self.current = None + self.enabled = self.phy_width != 0 + self.direction = 'same-as' + self.other_output = None + +def get_settings(xrandr_args): cout, cin = popen2.popen2([xrandr]) cin.close() - settings = defaultdict(list) - current = {} - phy_width = 0 - phy_height = 0 + settings = {} output = '' - i = -1 + i = 0 for line in cout: line = line.rstrip() if 'connected' in line: @@ -85,39 +67,59 @@ def get_settings(): phy_width = data[1] phy_height = data[3] output = line.split(' ')[0] + settings[output] = Settings(i, output, phy_width, phy_height) i += 1 elif line[0] in ' *' and 'x' in line: try: - if ' x ' in line: - setting = Setting(output, line[1:]) - else: - setting = NewSetting(output, line, phy_width, phy_height) - settings[(i, output)].append(setting) + setting = Setting(line) + settings[output].append(setting) if '*' in line: - current[output] = setting + settings[output].current = setting except Exception, ex: print >>sys.stderr, "Failed to parse line '%s':\n%s" % \ (line.strip(), str(ex)) cout.close() - return (current, settings) -def settings_to_args(settings): + it = iter(xrandr_args.split(' ')) + + output = '' + while True: + try: + arg = it.next() + except StopIteration: + break + if arg == '--output': + try: + output = it.next() + except StopIteration: + # Invalid args: --output not followed by output name. + break + elif arg in {'--same-as', '--left-of', '--right-of', '--above', '--below'}: + settings[output].direction = arg[2:] + try: + settings[output].other_output = it.next() + except StopIteration: + # Invalid args: direction not followed by output name. + break + return settings + +def settings_to_args(output2settings): args = [] - for setting in settings: - args += ['--output', setting.output] - if setting.enabled: - args += ['--mode', '%dx%d' % (setting.width, setting.height)] + for output, settings in output2settings.iteritems(): + args += ['--output', output] + current = settings.current + if settings.enabled: + args += ['--mode', '%dx%d' % (current.width, current.height)] else: args.append('--off') - if setting.current_r is not None: - args += ['--rate', str(setting.current_r)] - if setting.other_output is not None: - args += ['--%s' % setting.direction, setting.other_output] - + if current.current_r is not None: + args += ['--rate', str(current.current_r)] + if settings.other_output is not None: + args += ['--%s' % settings.direction, settings.other_output] return args -def set_modes(settings): - cmd = [xrandr] + settings_to_args(settings) +def set_modes(output2settings): + cmd = [xrandr] + settings_to_args(output2settings) cerr, cin = popen2.popen4(cmd) cin.close() errors = cerr.read() From d0b14ddb1f54d3625e11d83c161459a39b67860d Mon Sep 17 00:00:00 2001 From: Dennis Tomas Date: Tue, 31 Mar 2015 11:53:13 +0200 Subject: [PATCH 10/22] Removed unused import. --- xrandr.py | 1 - 1 file changed, 1 deletion(-) diff --git a/xrandr.py b/xrandr.py index fa5d056..4379dc2 100644 --- a/xrandr.py +++ b/xrandr.py @@ -1,5 +1,4 @@ import os, popen2, sys -from collections import defaultdict import rox paths = os.environ.get('PATH', '/bin:/usr/bin').split(':') From 1b41725fcb0035fd3ad28d46da03644fb6436d6e Mon Sep 17 00:00:00 2001 From: Dennis Tomas Date: Tue, 31 Mar 2015 11:58:28 +0200 Subject: [PATCH 11/22] Select configured output. --- AppRun | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/AppRun b/AppRun index 4d22948..81794e2 100755 --- a/AppRun +++ b/AppRun @@ -161,7 +161,9 @@ class ResBox(rox.Dialog): menu.append(item) if settings.other_output is None: settings.other_output = other_output - self.other_outputs[output].set_history(0) + self.other_outputs[output].set_history( + self.other_output_strings[output].index(settings.other_output) + ) self.other_outputs[output].connect("changed", self.other_output_selected, settings) From 325853dbf9133bebd54d3a3e5e592354648f8eee Mon Sep 17 00:00:00 2001 From: Dennis Tomas Date: Tue, 31 Mar 2015 12:04:08 +0200 Subject: [PATCH 12/22] Preferred resolution. --- AppRun | 15 +++++++++------ xrandr.py | 3 +++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/AppRun b/AppRun index 81794e2..4420b25 100755 --- a/AppRun +++ b/AppRun @@ -125,12 +125,6 @@ class ResBox(rox.Dialog): self.physical_size[output] = Field(_('Physical size: '), sg) vbox.pack_start(self.physical_size[output], False, True, 0) - try: - i = settings.index(settings.current) - except (ValueError, KeyError): - i = 0 - self.resolutions[output].set_history(i) - if settings.output_index > 0: hbox = g.HBox(False, 4) vbox.pack_start(hbox, False, True, 0) @@ -167,6 +161,15 @@ class ResBox(rox.Dialog): self.other_outputs[output].connect("changed", self.other_output_selected, settings) + try: + i = settings.index(settings.current) + except (ValueError, KeyError): + try: + i = settings.index(settings.preferred) + except (ValueError, KeyError): + i = 0 + self.resolutions[output].set_history(i) + self.vbox.show_all() def direction_changed(self, directions, settings): diff --git a/xrandr.py b/xrandr.py index 4379dc2..bb1b397 100644 --- a/xrandr.py +++ b/xrandr.py @@ -43,6 +43,7 @@ def __init__(self, output_index, output, phy_width, phy_height): self.phy_width = phy_width self.phy_height = phy_height self.current = None + self.preferred = None self.enabled = self.phy_width != 0 self.direction = 'same-as' self.other_output = None @@ -74,6 +75,8 @@ def get_settings(xrandr_args): settings[output].append(setting) if '*' in line: settings[output].current = setting + if '+' in line: + settings[output].preferred = setting except Exception, ex: print >>sys.stderr, "Failed to parse line '%s':\n%s" % \ (line.strip(), str(ex)) From 03c71b5142fd2a5381a9c14476b02d27e179d189 Mon Sep 17 00:00:00 2001 From: Dennis Tomas Date: Tue, 31 Mar 2015 12:06:09 +0200 Subject: [PATCH 13/22] Ignore disconnected outputs. --- xrandr.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/xrandr.py b/xrandr.py index bb1b397..55a5a56 100644 --- a/xrandr.py +++ b/xrandr.py @@ -97,9 +97,14 @@ def get_settings(xrandr_args): # Invalid args: --output not followed by output name. break elif arg in {'--same-as', '--left-of', '--right-of', '--above', '--below'}: - settings[output].direction = arg[2:] try: - settings[output].other_output = it.next() + output_settings = settings[output] + except KeyError: + # Configured output not connected. + continue + output_settings.direction = arg[2:] + try: + output_settings.other_output = it.next() except StopIteration: # Invalid args: direction not followed by output name. break From e5b4f300fe20d2648d95b9e420b7310244b053f6 Mon Sep 17 00:00:00 2001 From: Dennis Tomas Date: Tue, 31 Mar 2015 12:11:01 +0200 Subject: [PATCH 14/22] Preferred refresh rate. --- AppRun | 8 ++++++++ xrandr.py | 5 ++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/AppRun b/AppRun index 4420b25..21fff18 100755 --- a/AppRun +++ b/AppRun @@ -121,6 +121,14 @@ class ResBox(rox.Dialog): menu = g.Menu() self.refresh_rates[output].set_menu(menu) + i = 0 + current = settings.current + if current is not None: + if current.current_r is not None: + i = current.res.index(current.current_r) + if current.preferred_r is not None: + i = current.res.index(current.preferred_r) + self.refresh_rates[output].set_history(i) self.physical_size[output] = Field(_('Physical size: '), sg) vbox.pack_start(self.physical_size[output], False, True, 0) diff --git a/xrandr.py b/xrandr.py index 55a5a56..26f6e1e 100644 --- a/xrandr.py +++ b/xrandr.py @@ -23,13 +23,16 @@ def __init__(self, line): self.height = int(self.height) self.res = [] self.current_r = None + self.preferred_r = None for r in bits[1:]: try: res = float(r.strip('*+')) except ValueError: continue - if r.endswith('*'): + if '*' in r: self.current_r = res + if '+' in r: + self.preferred_r = res self.res.append(res) def __str__(self): From 3b389c11a23eb998be272d7c4b6174a6c394840c Mon Sep 17 00:00:00 2001 From: Dennis Tomas Date: Tue, 31 Mar 2015 12:23:10 +0200 Subject: [PATCH 15/22] Make disabled output selects insensitive. --- AppRun | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/AppRun b/AppRun index 21fff18..07a2823 100755 --- a/AppRun +++ b/AppRun @@ -72,13 +72,16 @@ class ResBox(rox.Dialog): self.directions = {} self.other_outputs = {} self.other_output_strings = {} + self.settings_vboxes = {} for output, settings in sorted(self.settings.iteritems(), key=lambda x: x[1].output_index): frame = g.Frame(output) - vbox = g.VBox(False, 4) + self.settings_vboxes[output] = vbox = g.VBox(False, 4) vbox.set_border_width(5) - frame.add(vbox) + vbox2 = g.VBox(False, 4) + frame.add(vbox2) + vbox2.pack_end(vbox) if len(self.settings) > 1: self.enabled[output] = g.CheckButton(_('Enabled')) @@ -88,7 +91,7 @@ class ResBox(rox.Dialog): ] self.enabled[output].set_sensitive(not settings.enabled or len(enabled_settings) > 1) self.enabled[output].connect('toggled', self.enabled_toggled, settings) - vbox.pack_start(self.enabled[output]) + vbox2.pack_start(self.enabled[output]) outputs_hbox.pack_start(frame, False, True, 0) hbox = g.HBox(False, 4) @@ -178,6 +181,8 @@ class ResBox(rox.Dialog): i = 0 self.resolutions[output].set_history(i) + vbox.set_sensitive(settings.enabled) + self.vbox.show_all() def direction_changed(self, directions, settings): @@ -204,6 +209,8 @@ class ResBox(rox.Dialog): for checkbutton in self.enabled.itervalues(): checkbutton.set_sensitive(True) + self.settings_vboxes[settings.output].set_sensitive(settings.enabled) + def show_details(self, resolutions, settings): settings.current = current = settings[resolutions.get_history()] phy_size = '%s x %s' % (settings.phy_width, settings.phy_height) From 5bcfa8cac101cf173c0c98a2d94f4edc0e8704d8 Mon Sep 17 00:00:00 2001 From: Dennis Tomas Date: Tue, 31 Mar 2015 15:56:26 +0200 Subject: [PATCH 16/22] Revert and Save buttons. --- AppRun | 16 ++++++++++++++-- xrandr.py | 22 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/AppRun b/AppRun index 07a2823..edbdcf6 100755 --- a/AppRun +++ b/AppRun @@ -26,6 +26,7 @@ class Field(g.HBox): self.value.set_padding(2, 2) RESPONSE_REFRESH = 1000 +RESPONSE_REVERT = 1001 other_direction = {0: 0, 1: 2, 2: 1, 3: 4, 4: 3} direction_strings = { @@ -39,13 +40,24 @@ class ResBox(rox.Dialog): self.add_button(g.STOCK_HELP, g.RESPONSE_HELP) self.add_button(g.STOCK_CLOSE, g.RESPONSE_CANCEL) self.add_button(g.STOCK_REFRESH, RESPONSE_REFRESH) - self.add_button(g.STOCK_APPLY, g.RESPONSE_OK) - self.set_default_response(g.RESPONSE_OK) + self.add_button(g.STOCK_REVERT_TO_SAVED, RESPONSE_REVERT) + self.add_button(g.STOCK_APPLY, g.RESPONSE_APPLY) + self.add_button(g.STOCK_SAVE, g.RESPONSE_OK) + self.set_default_response(g.RESPONSE_APPLY) self.set_has_separator(False) def resp(d, r): if r == int(g.RESPONSE_HELP): from rox import filer filer.open_dir(os.path.join(rox.app_dir, 'Help')) + elif r == int(g.RESPONSE_APPLY): + xrandr.set_modes(self.settings) + self.hide() + self.present() + elif r == int(RESPONSE_REVERT): + self.destroy() + res_box = ResBox() + res_box.show() + xrandr.set_modes(res_box.settings) elif r == int(g.RESPONSE_OK): xrandr.set_modes(self.settings) xrandr_setting._set(' '.join(xrandr.settings_to_args(self.settings))) diff --git a/xrandr.py b/xrandr.py index 26f6e1e..40263e3 100644 --- a/xrandr.py +++ b/xrandr.py @@ -111,6 +111,28 @@ def get_settings(xrandr_args): except StopIteration: # Invalid args: direction not followed by output name. break + elif arg == '--mode': + try: + width, height = [int(s) for s in it.next().split('x')] + except (StopIteration, ValueError): + # Invalid args: --mode not followed by valid mode string. + break + else: + for setting in settings[output]: + if setting.width == width and setting.height == height: + settings[output].current = setting + break + elif arg == '--rate': + # This code relies on --mode being set before --rate, + # so settings[output].current is set to the setting currently + # saved. + try: + rate = float(it.next()) + except (StopIteration, ValueError): + # Invalid args: --rate not followed by valid rate string. + break + else: + settings[output].current.current_r = rate return settings def settings_to_args(output2settings): From 748fca6b6d5905f8089d8120aab260a60f0d26f6 Mon Sep 17 00:00:00 2001 From: Dennis Tomas Date: Tue, 31 Mar 2015 16:03:57 +0200 Subject: [PATCH 17/22] Cope with disabled outputs. --- xrandr.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/xrandr.py b/xrandr.py index 40263e3..470badf 100644 --- a/xrandr.py +++ b/xrandr.py @@ -132,7 +132,11 @@ def get_settings(xrandr_args): # Invalid args: --rate not followed by valid rate string. break else: - settings[output].current.current_r = rate + try: + settings[output].current.current_r = rate + except (KeyError, AttributeError): + # Output disabled. + continue return settings def settings_to_args(output2settings): From 8a64009c03b9b5edb32488fa4d751875128df5c1 Mon Sep 17 00:00:00 2001 From: Dennis Tomas Date: Tue, 31 Mar 2015 16:05:17 +0200 Subject: [PATCH 18/22] Set enabled to False on disabled outputs. --- xrandr.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xrandr.py b/xrandr.py index 470badf..f6ef07b 100644 --- a/xrandr.py +++ b/xrandr.py @@ -122,6 +122,8 @@ def get_settings(xrandr_args): if setting.width == width and setting.height == height: settings[output].current = setting break + elif arg == '--off': + settings[output].enabled = False elif arg == '--rate': # This code relies on --mode being set before --rate, # so settings[output].current is set to the setting currently From f24141febb1e78bb515205253077cac22c97d07c Mon Sep 17 00:00:00 2001 From: Dennis Tomas Date: Tue, 31 Mar 2015 16:18:06 +0200 Subject: [PATCH 19/22] Position label. --- AppRun | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/AppRun b/AppRun index edbdcf6..88754db 100755 --- a/AppRun +++ b/AppRun @@ -152,7 +152,10 @@ class ResBox(rox.Dialog): hbox = g.HBox(False, 4) vbox.pack_start(hbox, False, True, 0) self.directions[output] = g.OptionMenu() - sg.add_widget(self.directions[output]) + label = g.Label(_('Position: ')) + sg.add_widget(label) + label.set_alignment(1, 0.5) + hbox.pack_start(label, False, True, 0) hbox.pack_start(self.directions[output], False, True, 0) menu = g.Menu() self.directions[output].set_menu(menu) From 53b5849819c949bf2a17e3f677929df9d80c3961 Mon Sep 17 00:00:00 2001 From: Dennis Tomas Date: Tue, 31 Mar 2015 16:18:48 +0200 Subject: [PATCH 20/22] German translation. --- Messages/de.gmo | Bin 0 -> 952 bytes Messages/de.po | 64 ++++++++++++++++++++++++++++++++++++++++++++ Messages/dist | 8 ++++++ Messages/update-po | 16 +++++++++++ messages.pot | 65 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 153 insertions(+) create mode 100644 Messages/de.gmo create mode 100644 Messages/de.po create mode 100755 Messages/dist create mode 100755 Messages/update-po create mode 100644 messages.pot diff --git a/Messages/de.gmo b/Messages/de.gmo new file mode 100644 index 0000000000000000000000000000000000000000..14975548d839886df9f0497720ae04ccf22d107e GIT binary patch literal 952 zcmZ9J&ubGw6vs!czhYG^f<1`LMS3V3vzAs_Tj@60#?Yh*Nm|r{Fx`E-J9aY@XC`S= zPl5-Jf_PIr2p&E5Ead<2FYu4>O|mxNz~eJ-_Py_WZ{P2Qxz7a8b+o%^@6m3d{Xw&* zbdHdVNH^yr`4M>q`4wsNe;{q0pU54wUud?@ z{DmnOaxNn8;QKw~RpeUkZz64-=eZvuZJoFI`v;`$=M(Za@;eesoE=-s>RAmtyY1~7 znmy;yE=_KPU>cc2v)Zhbm_icB6Wxj zl`(0hBh5pWQO!@lqogG@H(Uz5wjoxaQ`%<6erbc2+2G$do}`of4(kDBnsh=1Am}WJ zw3K`ep>b-Fvkyv7G+Tao$FBx-KWKMq&D}z)*>%bam{oNulR6Je?y6H-aaLUFu6ge2 zayjU9G0HYSjlJ%+^LQ3#8;e0Xfe2;9MdHzEKotZi96xflMjq{t z5-?V+*dI~1xLP3NF9)E=9#&JtKQw$uwpp5iqC+YE+bb7`+CEP=d&n}b!xU&$jZeql z{@d^t&!Qs^5vWvVxa>Cr5GS~5e-LNmQ$4T^;@-p5OeHHEXp#Z9f|y5aKB{xoV`mxU EFGsKE{{R30 literal 0 HcmV?d00001 diff --git a/Messages/de.po b/Messages/de.po new file mode 100644 index 0000000..e61e715 --- /dev/null +++ b/Messages/de.po @@ -0,0 +1,64 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2015-03-31 16:15+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Dennis Tomas \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: UTF-8\n" +"Generated-By: pygettext.py 1.5\n" + +#: AppRun:39 +msgid "Screen Resolution" +msgstr "Bildschirmauflösung" + +#: AppRun:99 +msgid "Enabled" +msgstr "Aktiv" + +#: AppRun:111 +msgid "Resolution: " +msgstr "Auflösung: " + +#: AppRun:128 +msgid "Refresh Rate: " +msgstr "Bildwiederholfrequenz: " + +#: AppRun:148 +msgid "Physical size: " +msgstr "Physikalische Größe: " + +#: AppRun:155 +msgid "Position: " +msgstr "Position: " + +#: AppRun:162 +msgid "Same as" +msgstr "Die gleiche wie" + +#: AppRun:163 +msgid "Left of" +msgstr "Links von" + +#: AppRun:164 +msgid "Right of" +msgstr "Rechts von" + +#: AppRun:165 +msgid "Above" +msgstr "Über" + +#: AppRun:166 +msgid "Below" +msgstr "Unter" + +#: xrandr.py:165 +msgid "Errors from xrandr: '%s'" +msgstr "Fahler von xrandr: '%s'" diff --git a/Messages/dist b/Messages/dist new file mode 100755 index 0000000..a637e09 --- /dev/null +++ b/Messages/dist @@ -0,0 +1,8 @@ +#!/bin/sh + +echo Making all .mo files... +echo + +for FILE in *.po; do + msgfmt $FILE -o ${FILE%.po}.gmo && echo Created file $OUT OK +done diff --git a/Messages/update-po b/Messages/update-po new file mode 100755 index 0000000..300a331 --- /dev/null +++ b/Messages/update-po @@ -0,0 +1,16 @@ +#!/bin/sh + +echo Extracting messages from source files... +echo + +(cd ..; pygettext *.py AppRun) + +echo +echo Updating all .po files... +echo + +for FILE in *.po; do + echo -n "Updating '$FILE' translation" + mv $FILE $FILE.old + msgmerge $FILE.old ../messages.pot > $FILE +done diff --git a/messages.pot b/messages.pot new file mode 100644 index 0000000..1d583f4 --- /dev/null +++ b/messages.pot @@ -0,0 +1,65 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2015-03-31 16:15+CEST\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: ENCODING\n" +"Generated-By: pygettext.py 1.5\n" + + +#: AppRun:39 +msgid "Screen Resolution" +msgstr "" + +#: AppRun:99 +msgid "Enabled" +msgstr "" + +#: AppRun:111 +msgid "Resolution: " +msgstr "" + +#: AppRun:128 +msgid "Refresh Rate: " +msgstr "" + +#: AppRun:148 +msgid "Physical size: " +msgstr "" + +#: AppRun:155 +msgid "Position: " +msgstr "" + +#: AppRun:162 +msgid "Same as" +msgstr "" + +#: AppRun:163 +msgid "Left of" +msgstr "" + +#: AppRun:164 +msgid "Right of" +msgstr "" + +#: AppRun:165 +msgid "Above" +msgstr "" + +#: AppRun:166 +msgid "Below" +msgstr "" + +#: xrandr.py:165 +msgid "Errors from xrandr: '%s'" +msgstr "" + From 567d8bd651b0fa44e4ed688f6bdc3d0d88554061 Mon Sep 17 00:00:00 2001 From: Dennis Tomas Date: Tue, 31 Mar 2015 16:29:40 +0200 Subject: [PATCH 21/22] Deal with disconnected outputs in saved config. --- xrandr.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/xrandr.py b/xrandr.py index f6ef07b..ddcb6a6 100644 --- a/xrandr.py +++ b/xrandr.py @@ -123,7 +123,11 @@ def get_settings(xrandr_args): settings[output].current = setting break elif arg == '--off': - settings[output].enabled = False + try: + settings[output].enabled = False + except KeyError: + # The output has been disconnected. + continue elif arg == '--rate': # This code relies on --mode being set before --rate, # so settings[output].current is set to the setting currently From 82f93483b7dd79a1cdfe6c24d1d8d33fcc779275 Mon Sep 17 00:00:00 2001 From: Dennis Tomas Date: Fri, 11 Mar 2016 22:49:59 +0100 Subject: [PATCH 22/22] Fixed ValueError. --- AppRun | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/AppRun b/AppRun index 88754db..b952a32 100755 --- a/AppRun +++ b/AppRun @@ -140,9 +140,15 @@ class ResBox(rox.Dialog): current = settings.current if current is not None: if current.current_r is not None: - i = current.res.index(current.current_r) + try: + i = current.res.index(current.current_r) + except ValueError: + pass if current.preferred_r is not None: - i = current.res.index(current.preferred_r) + try: + i = current.res.index(current.preferred_r) + except ValueError: + pass self.refresh_rates[output].set_history(i) self.physical_size[output] = Field(_('Physical size: '), sg) @@ -238,7 +244,8 @@ class ResBox(rox.Dialog): item.show() self.refresh_rates[settings.output].set_history( current.res.index(current.current_r) - if current.current_r is not None else 0 + if current.current_r is not None + and current.current_r in current.res else 0 )