1#!/usr/local/bin/python3.8 2 3import gi 4gi.require_version('UPowerGlib', '1.0') 5from gi.repository import UPowerGlib 6 7from SettingsWidgets import SidePage 8from xapp.GSettingsWidgets import * 9 10POWER_BUTTON_OPTIONS = [ 11 ("blank", _("Lock the screen")), 12 ("suspend", _("Suspend")), 13 ("shutdown", _("Shut down immediately")), 14 ("hibernate", _("Hibernate")), 15 ("interactive", _("Ask what to do")), 16 ("nothing", _("Do nothing")) 17] 18 19IDLE_BRIGHTNESS_OPTIONS = [ 20 (5, _("5%")), 21 (10, _("10%")), 22 (30, _("30%")), 23 (50, _("50%")), 24 (75, _("75%")) 25] 26 27IDLE_DELAY_OPTIONS = [ 28 (30, _("30 seconds")), 29 (60, _("60 seconds")), 30 (90, _("90 seconds")), 31 (120, _("2 minutes")), 32 (300, _("5 minutes")), 33 (600, _("10 minutes")) 34] 35 36SLEEP_DELAY_OPTIONS = [ 37 (300, _("5 minutes")), 38 (600, _("10 minutes")), 39 (900, _("15 minutes")), 40 (1800, _("30 minutes")), 41 (2700, _("45 minutes")), 42 (3600, _("1 hour")), 43 (7200, _("2 hours")), 44 (10800, _("3 hours")), 45 (0, _("Never")) 46] 47 48(UP_ID, UP_VENDOR, UP_MODEL, UP_TYPE, UP_ICON, UP_PERCENTAGE, UP_STATE, UP_BATTERY_LEVEL, UP_SECONDS) = range(9) 49 50try: 51 UPowerGlib.DeviceLevel 52except AttributeError: 53 class DeviceLevel: 54 UNKNOWN = 1 55 NONE = 1 56 LOW = 3 57 CRITICAL = 4 58 NORMAL = 6 59 HIGH = 7 60 FULL = 9 61 UPowerGlib.DeviceLevel = DeviceLevel 62 63 64def get_timestring(time_seconds): 65 minutes = int((time_seconds / 60.0) + 0.5) 66 67 if minutes == 0: 68 time_string = _("Unknown time") 69 return time_string 70 71 if minutes < 60: 72 if minutes == 1: 73 time_string = ("%d " % minutes) + _("minute") 74 else: 75 time_string = ("%d " % minutes) + _("minutes") 76 return time_string 77 78 hours = minutes / 60 79 minutes = minutes % 60 80 81 if minutes == 0: 82 if hours == 1: 83 time_string = ("%d " % hours) + _("hour") 84 return time_string 85 else: 86 time_string = ("%d " % hours) + _("hours") 87 return time_string 88 89 if hours == 1: 90 if minutes == 1: 91 time_string = ("%d " % hours + _("hour")) + (" %d " % minutes + _("minute")) 92 return time_string 93 else: 94 time_string = ("%d " % hours + _("hour")) + (" %d " % minutes + _("minutes")) 95 return time_string 96 97 time_string = ("%d " % hours + _("hours")) + (" %d " % minutes + _("minutes")) 98 return time_string 99 100 101CSD_SCHEMA = "org.cinnamon.settings-daemon.plugins.power" 102CSM_SCHEMA = "org.cinnamon.SessionManager" 103 104class Module: 105 name = "power" 106 category = "hardware" 107 comment = _("Manage power settings") 108 109 def __init__(self, content_box): 110 keywords = _("power, suspend, hibernate, laptop, desktop, brightness, screensaver") 111 self.sidePage = SidePage(_("Power Management"), "cs-power", keywords, content_box, -1, module=self) 112 113 def on_module_selected(self): 114 if self.loaded: 115 # self.loaded = False 116 return 117 print("Loading Power module") 118 119 self.up_client = UPowerGlib.Client.new() 120 121 self.csd_power_proxy = Gio.DBusProxy.new_sync( 122 Gio.bus_get_sync(Gio.BusType.SESSION, None), 123 Gio.DBusProxyFlags.NONE, 124 None, 125 "org.cinnamon.SettingsDaemon.Power", 126 "/org/cinnamon/SettingsDaemon/Power", 127 "org.cinnamon.SettingsDaemon.Power", 128 None) 129 130 self.settings = Gio.Settings.new("org.cinnamon") 131 132 device_types = [x[UP_TYPE] for x in self.csd_power_proxy.GetDevices()] 133 134 self.has_battery = UPowerGlib.DeviceKind.BATTERY in device_types or UPowerGlib.DeviceKind.UPS in device_types 135 self.has_lid = self.up_client.get_lid_is_present() 136 137 self.sidePage.stack = SettingsStack() 138 139 # Power 140 141 power_page = SettingsPage() 142 143 section = power_page.add_section(_("Power Options")) 144 145 lid_options, button_power_options, critical_options, can_suspend, can_hybrid_sleep, can_hibernate = get_available_options(self.up_client) 146 147 size_group = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL) 148 149 if self.has_battery: 150 header = SettingsWidget() 151 label_ac = Gtk.Label() 152 label_ac.set_markup("<b>%s</b>" % _("On A/C power")) 153 size_group.add_widget(label_ac) 154 label_battery = Gtk.Label() 155 label_battery.set_markup("<b>%s</b>" % _("On battery power")) 156 size_group.add_widget(label_battery) 157 header.pack_end(label_battery, False, False, 0) 158 header.pack_end(label_ac, False, False, 0) 159 160 section.add_row(header) 161 162 section.add_row(GSettings2ComboBox(_("Turn off the screen when inactive for"), CSD_SCHEMA, "sleep-display-ac", "sleep-display-battery", SLEEP_DELAY_OPTIONS, valtype="int", size_group=size_group)) 163 164 section.add_row(GSettings2ComboBox(_("Suspend when inactive for"), CSD_SCHEMA, "sleep-inactive-ac-timeout", "sleep-inactive-battery-timeout", SLEEP_DELAY_OPTIONS, valtype="int", size_group=size_group)) 165 166 if self.has_lid: 167 section.add_row(GSettings2ComboBox(_("When the lid is closed"), CSD_SCHEMA, "lid-close-ac-action", "lid-close-battery-action", lid_options, size_group=size_group)) 168 169 else: 170 section.add_row(GSettingsComboBox(_("Turn off the screen when inactive for"), CSD_SCHEMA, "sleep-display-ac", SLEEP_DELAY_OPTIONS, valtype=int, size_group=size_group)) 171 172 section.add_row(GSettingsComboBox(_("Suspend when inactive for"), CSD_SCHEMA, "sleep-inactive-ac-timeout", SLEEP_DELAY_OPTIONS, valtype=int, size_group=size_group)) 173 174 if self.has_lid: 175 section.add_row(GSettingsComboBox(_("When the lid is closed"), CSD_SCHEMA, "lid-close-ac-action", lid_options, size_group=size_group)) 176 177 section = power_page.add_section(_("Extra options")) 178 179 size_group = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL) 180 181 section.add_row(GSettingsComboBox(_("When the power button is pressed"), CSD_SCHEMA, "button-power", button_power_options, size_group=size_group)) 182 183 if self.has_lid: 184 section.add_row(GSettingsSwitch(_("Perform lid-closed action even with external monitors attached"), CSD_SCHEMA, "lid-close-suspend-with-external-monitor")) 185 186 if self.has_battery and UPowerGlib.MAJOR_VERSION == 0 and UPowerGlib.MINOR_VERSION <= 99: 187 section.add_row(GSettingsComboBox(_("When the battery is critically low"), CSD_SCHEMA, "critical-battery-action", critical_options, size_group=size_group)) 188 189 if can_suspend and can_hybrid_sleep: 190 self.hybrid_switch = GSettingsSwitch(_("Enable Hybrid Sleep"), CSM_SCHEMA, "prefer-hybrid-sleep") 191 self.hybrid_switch.set_tooltip_text(_("Replaces Suspend with Hybrid Sleep")) 192 self.hybrid_switch.content_widget.connect("notify::active", self.on_hybrid_toggled) 193 section.add_row(self.hybrid_switch) 194 195 if can_suspend and can_hibernate: 196 self.sth_switch = GSettingsSwitch(_("Enable Hibernate after suspend"), CSM_SCHEMA, "suspend-then-hibernate") 197 self.sth_switch.set_tooltip_text(_("First suspend the machine and hibernate it after a certain amount of time.")) 198 self.sth_switch.content_widget.connect("notify::active", self.on_sth_toggled) 199 section.add_row(self.sth_switch) 200 201 # Batteries 202 203 self.battery_page = SettingsPage() 204 self.show_battery_page = False 205 self.battery_label_size_group = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL) 206 207 self.build_battery_page() 208 self.csd_power_proxy.connect("g-properties-changed", self.build_battery_page) 209 210 proxy = Gio.DBusProxy.new_sync( 211 Gio.bus_get_sync(Gio.BusType.SESSION, None), 212 Gio.DBusProxyFlags.NONE, 213 None, 214 "org.cinnamon.SettingsDaemon.Power", 215 "/org/cinnamon/SettingsDaemon/Power", 216 "org.cinnamon.SettingsDaemon.Power.Screen", 217 None) 218 219 try: 220 brightness = proxy.GetPercentage() 221 except GLib.Error as e: 222 print("Power module brightness page not available: %s" % e.message) 223 224 if self.show_battery_page: 225 self.sidePage.add_widget(self.sidePage.stack) 226 self.sidePage.stack.add_titled(power_page, "power", _("Power")) 227 self.sidePage.stack.add_titled(self.battery_page, "batteries", _("Batteries")) 228 else: 229 self.sidePage.add_widget(power_page) 230 else: 231 self.sidePage.add_widget(self.sidePage.stack) 232 self.sidePage.stack.add_titled(power_page, "power", _("Power")) 233 if self.show_battery_page: 234 self.sidePage.stack.add_titled(self.battery_page, "batteries", _("Batteries")) 235 236 page = SettingsPage() 237 self.sidePage.stack.add_titled(page, "brightness", _("Brightness")) 238 239 size_group = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL) 240 241 section = page.add_section(_("Screen brightness")) 242 section.add_row(BrightnessSlider(section, proxy, _("Screen brightness"))) 243 244 section.add_row(GSettingsSwitch(_("On battery, dim screen when inactive"), CSD_SCHEMA, "idle-dim-battery")) 245 246 section.add_reveal_row(GSettingsComboBox(_("Brightness level when inactive"), CSD_SCHEMA, "idle-brightness", IDLE_BRIGHTNESS_OPTIONS, valtype=int, size_group=size_group), CSD_SCHEMA, "idle-dim-battery") 247 248 section.add_reveal_row(GSettingsComboBox(_("Dim screen after inactive for"), CSD_SCHEMA, "idle-dim-time", IDLE_DELAY_OPTIONS, valtype=int, size_group=size_group), CSD_SCHEMA, "idle-dim-battery") 249 250 proxy = Gio.DBusProxy.new_sync(Gio.bus_get_sync(Gio.BusType.SESSION, None), 251 Gio.DBusProxyFlags.NONE, 252 None, 253 "org.cinnamon.SettingsDaemon.Power", 254 "/org/cinnamon/SettingsDaemon/Power", 255 "org.cinnamon.SettingsDaemon.Power.Keyboard", 256 None) 257 258 try: 259 brightness = proxy.GetPercentage() 260 except GLib.Error as e: 261 print("Power module no keyboard backlight: %s" % e.message) 262 else: 263 section = page.add_section(_("Keyboard backlight")) 264 section.add_row(BrightnessSlider(section, proxy, _("Backlight brightness"))) 265 266 def on_sth_toggled(self, widget, gparam): 267 active = widget.get_active() 268 if active and hasattr(self, "hybrid_switch"): 269 self.hybrid_switch.set_value(False) 270 271 def on_hybrid_toggled(self, widget, gparam): 272 active = widget.get_active() 273 if active and hasattr(self, "sth_switch"): 274 self.sth_switch.set_value(False) 275 276 def build_battery_page(self, *args): 277 278 self.aliases = {} 279 device_aliases = self.settings.get_strv("device-aliases") 280 for alias in device_aliases: 281 try: 282 (device_id, device_nickname) = alias.split(":=") 283 self.aliases[device_id] = device_nickname 284 except: 285 pass # ignore malformed aliases 286 287 #destroy all widgets in this page 288 for widget in self.battery_page.get_children(): 289 widget.destroy() 290 291 secondary_settings = None 292 primary_settings = None 293 294 # UPowerGlib segfaults when trying to get device. Use CSD instead 295 devices = self.csd_power_proxy.GetDevices() 296 297 have_primary = False 298 ups_as_primary = False 299 300 # first we look for a discharging UPS, which is promoted to the 301 # primary device if it's discharging. Otherwise we use the first 302 # listed laptop battery as the primary device 303 304 for device in devices: 305 if device[UP_TYPE] == UPowerGlib.DeviceKind.UPS and device[UP_STATE] == UPowerGlib.DeviceState.DISCHARGING: 306 ups_as_primary = True 307 308 for device in devices: 309 if device[UP_TYPE] == UPowerGlib.DeviceKind.LINE_POWER: 310 pass # Do nothing 311 elif device[UP_TYPE] == UPowerGlib.DeviceKind.UPS and ups_as_primary: 312 if not primary_settings: 313 primary_settings = self.battery_page.add_section(_("Batteries")) 314 primary_settings.add_row(self.set_device_ups_primary(device)) 315 self.show_battery_page = True 316 else: 317 primary_settings.add_row(self.set_device_ups_primary(device)) 318 elif device[UP_TYPE] == UPowerGlib.DeviceKind.BATTERY and not ups_as_primary: 319 if not have_primary: 320 if not primary_settings: 321 primary_settings = self.battery_page.add_section(_("Batteries")) 322 primary_settings.add_row(self.set_device_battery_primary(device)) 323 self.show_battery_page = True 324 have_primary = True 325 else: 326 widget = self.set_device_battery_additional(device) 327 if widget: 328 primary_settings.add_row(widget) 329 else: 330 if not secondary_settings: 331 secondary_settings = self.battery_page.add_section(_("Devices")) 332 secondary_settings.add_row(self.add_battery_device_secondary(device)) 333 self.show_battery_page = True 334 else: 335 secondary_settings.add_row(self.add_battery_device_secondary(device)) 336 337 #show all the widgets in this page, but not the page itself 338 visible = self.battery_page.get_visible() 339 self.battery_page.show_all() 340 self.battery_page.set_visible(visible) 341 342 def set_device_ups_primary(self, device): 343 device_id = device[UP_ID] 344 percentage = device[UP_PERCENTAGE] 345 battery_level = device[UP_BATTERY_LEVEL] 346 state = device[UP_STATE] 347 time = device[UP_SECONDS] 348 vendor = device[UP_VENDOR] 349 model = device[UP_MODEL] 350 details = None 351 352 if time > 0: 353 time_string = get_timestring(time) 354 355 if state == UPowerGlib.DeviceState.DISCHARGING: 356 if percentage < 20: 357 details = _("Caution low UPS, %s remaining") % time_string 358 else: 359 details = _("Using UPS power - %s remaining") % time_string 360 else: 361 details = UPowerGlib.Device.state_to_string(state) 362 else: 363 if state == UPowerGlib.DeviceState.DISCHARGING: 364 if percentage < 20: 365 details = _("Caution low UPS") 366 else: 367 details = _("Using UPS power") 368 else: 369 details = UPowerGlib.Device.state_to_string(state) 370 371 desc = _("UPS") 372 if (model != "" or vendor != ""): 373 desc = "%s %s" % (vendor, model) 374 375 widget = self.create_battery_row(device_id, "battery", desc, percentage, battery_level, details) 376 return widget 377 378 def set_device_battery_primary(self, device): 379 device_id = device[UP_ID] 380 percentage = device[UP_PERCENTAGE] 381 battery_level = device[UP_BATTERY_LEVEL] 382 state = device[UP_STATE] 383 time = device[UP_SECONDS] 384 vendor = device[UP_VENDOR] 385 model = device[UP_MODEL] 386 details = None 387 388 if time > 0: 389 time_string = get_timestring(time) 390 391 if state == UPowerGlib.DeviceState.CHARGING or state == UPowerGlib.DeviceState.PENDING_CHARGE: 392 details = _("Charging - %s until fully charged") % time_string 393 elif state == UPowerGlib.DeviceState.DISCHARGING or state == UPowerGlib.DeviceState.PENDING_DISCHARGE: 394 if percentage < 20: 395 details = _("Caution low battery, %s remaining") % time_string 396 else: 397 details = _("Using battery power - %s remaining") % time_string 398 else: 399 details = UPowerGlib.Device.state_to_string(state) 400 else: 401 if state == UPowerGlib.DeviceState.CHARGING or state == UPowerGlib.DeviceState.PENDING_CHARGE: 402 details = _("Charging") 403 elif state == UPowerGlib.DeviceState.DISCHARGING or state == UPowerGlib.DeviceState.PENDING_DISCHARGE: 404 details = _("Using battery power") 405 elif state == UPowerGlib.DeviceState.FULLY_CHARGED: 406 details = _("Charging - fully charged") 407 elif state == UPowerGlib.DeviceState.EMPTY: 408 details = _("Empty") 409 else: 410 details = UPowerGlib.Device.state_to_string(state) 411 412 desc = _("Battery") 413 if (model != "" or vendor != ""): 414 desc = "%s %s" % (vendor, model) 415 416 widget = self.create_battery_row(device_id, "battery", desc, percentage, battery_level, details) 417 return widget 418 419 def set_device_battery_additional(self, device): 420 state = device[UP_STATE] 421 details = None 422 423 if state == UPowerGlib.DeviceState.FULLY_CHARGED: 424 details = _("Fully charged") 425 elif state == UPowerGlib.DeviceState.EMPTY: 426 details = _("Empty") 427 428 if details: 429 widget = SettingsWidget() 430 icon = Gtk.Image.new_from_icon_name("battery", Gtk.IconSize.DND) 431 widget.pack_start(icon, False, False, 0) 432 label = Gtk.Label(_("Secondary battery")) 433 widget.pack_start(label, False, False, 0) 434 label = Gtk.Label() 435 label.set_markup(details) 436 label.get_style_context().add_class("dim-label") 437 widget.pack_end(label, False, False, 0) 438 439 return widget 440 else: 441 return None 442 443 def add_battery_device_secondary(self, device): 444 device_id = device[UP_ID] 445 kind = device[UP_TYPE] 446 percentage = device[UP_PERCENTAGE] 447 battery_level = device[UP_BATTERY_LEVEL] 448 vendor = device[UP_VENDOR] 449 model = device[UP_MODEL] 450 451 if kind == UPowerGlib.DeviceKind.UPS: 452 icon_name = "uninterruptible-power-supply" 453 desc = _("Uninterruptible power supply") 454 elif kind == UPowerGlib.DeviceKind.MOUSE: 455 icon_name = "input-mouse" 456 desc = _("Wireless mouse") 457 elif kind == UPowerGlib.DeviceKind.KEYBOARD: 458 icon_name = "input-keyboard" 459 desc = _("Wireless Keyboard") 460 elif kind == UPowerGlib.DeviceKind.TABLET: 461 icon_name = "input-tablet" 462 desc = _("Tablet") 463 elif kind == UPowerGlib.DeviceKind.PDA: 464 icon_name = "pda" 465 desc = _("Personal digital assistant") 466 elif kind == UPowerGlib.DeviceKind.PHONE: 467 icon_name = "phone" 468 desc = _("Cellphone") 469 elif kind == UPowerGlib.DeviceKind.MEDIA_PLAYER: 470 icon_name = "multimedia-player" 471 desc = _("Media player") 472 elif kind == UPowerGlib.DeviceKind.COMPUTER: 473 icon_name = "computer" 474 desc = _("Computer") 475 else: 476 icon_name = "battery" 477 desc = (_("Battery")) 478 479 if (model != "" or vendor != ""): 480 desc = "%s %s" % (vendor, model) 481 482 widget = self.create_battery_row(device_id, icon_name, desc, percentage, battery_level) 483 return widget 484 485 def create_battery_row(self, device_id, icon_name, desc, percentage, battery_level, details=None): 486 487 if device_id in self.aliases: 488 desc = self.aliases[device_id] 489 490 widget = SettingsWidget() 491 492 vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) 493 hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) 494 label_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=15) 495 496 image = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.DND) 497 entry = Gtk.Entry() 498 entry.set_text(desc) 499 entry.connect('focus-out-event', self.on_alias_changed, device_id) 500 label_box.pack_start(image, False, False, 0) 501 label_box.pack_start(entry, False, False, 0) 502 self.battery_label_size_group.add_widget(label_box) 503 hbox.pack_start(label_box, False, False, 0) 504 505 if battery_level == UPowerGlib.DeviceLevel.NONE: 506 label = Gtk.Label() 507 label.set_markup("%d%%" % int(percentage)) 508 label.set_size_request(30, -1) 509 hbox.pack_start(label, False, False, 15) 510 511 level_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) 512 level_bar = Gtk.LevelBar() 513 level_bar.set_mode(Gtk.LevelBarMode.DISCRETE) 514 level_bar.set_min_value(0) 515 level_bar.set_max_value(10) 516 level_bar.add_offset_value("high", 5) 517 level_bar.add_offset_value("low", 2) 518 level_box.set_valign(Gtk.Align.CENTER) 519 level_bar.set_value(round(percentage / 10)) 520 level_box.pack_start(level_bar, True, True, 0) 521 hbox.pack_start(level_box, True, True, 0) 522 else: 523 status_label = Gtk.Label(self.bat_level_to_label(battery_level)) 524 hbox.pack_end(status_label, False, False, 0) 525 526 vbox.pack_start(hbox, False, False, 0) 527 528 if details: 529 label = Gtk.Label() 530 label.set_markup(details) 531 label.get_style_context().add_class("dim-label") 532 label.set_halign(Gtk.Align.END) 533 vbox.pack_end(label, False, False, 0) 534 535 widget.pack_start(vbox, True, True, 0) 536 537 return widget 538 539 def bat_level_to_label(self, level): 540 if level == UPowerGlib.DeviceLevel.FULL: 541 return _("Battery full") 542 elif level == UPowerGlib.DeviceLevel.HIGH: 543 return _("Battery almost full") 544 elif level == UPowerGlib.DeviceLevel.NORMAL: 545 return _("Battery good") 546 elif level == UPowerGlib.DeviceLevel.LOW: 547 return _("Low battery") 548 elif level == UPowerGlib.DeviceLevel.CRITICAL: 549 return _("Critically low battery") 550 551 def on_alias_changed(self, entry, event, device_id): 552 self.aliases[device_id] = entry.get_text() 553 aliases = [] 554 for alias in self.aliases: 555 aliases.append("%s:=%s" % (alias, self.aliases[alias])) 556 self.settings.set_strv("device-aliases", aliases) 557 558 559def get_available_options(up_client): 560 can_suspend = False 561 can_hibernate = False 562 can_hybrid_sleep = False 563 564 # Try logind first 565 try: 566 connection = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) 567 proxy = Gio.DBusProxy.new_sync( 568 connection, 569 Gio.DBusProxyFlags.NONE, 570 None, 571 "org.freedesktop.login1", 572 "/org/freedesktop/login1", 573 "org.freedesktop.login1.Manager", 574 None) 575 576 can_suspend = proxy.CanSuspend() == "yes" 577 can_hibernate = proxy.CanHibernate() == "yes" 578 can_hybrid_sleep = proxy.CanHybridSleep() == "yes" 579 except: 580 pass 581 582 # Next try ConsoleKit 583 try: 584 connection = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) 585 proxy = Gio.DBusProxy.new_sync( 586 connection, 587 Gio.DBusProxyFlags.NONE, 588 None, 589 "org.freedesktop.ConsoleKit", 590 "/org/freedesktop/ConsoleKit/Manager", 591 "org.freedesktop.ConsoleKit.Manager", 592 None) 593 594 can_suspend = can_suspend or (proxy.CanSuspend() == "yes") 595 can_hibernate = can_hibernate or (proxy.CanHybridSleep() == "yes") 596 can_hybrid_sleep = can_hybrid_sleep or (proxy.CanHybridSleep() == "yes") 597 except: 598 pass 599 600 def remove(options, item): 601 for option in options: 602 if option[0] == item: 603 options.remove(option) 604 break 605 606 lid_options = [ 607 ("suspend", _("Suspend")), 608 ("shutdown", _("Shut down immediately")), 609 ("hibernate", _("Hibernate")), 610 ("blank", _("Lock Screen")), 611 ("nothing", _("Do nothing")) 612 ] 613 614 button_power_options = [ 615 ("blank", _("Lock Screen")), 616 ("suspend", _("Suspend")), 617 ("shutdown", _("Shut down immediately")), 618 ("hibernate", _("Hibernate")), 619 ("interactive", _("Ask")), 620 ("nothing", _("Do nothing")) 621 ] 622 623 critical_options = [ 624 ("shutdown", _("Shut down immediately")), 625 ("hibernate", _("Hibernate")), 626 ("nothing", _("Do nothing")) 627 ] 628 629 if not can_suspend: 630 for options in lid_options, button_power_options, critical_options: 631 remove(options, "suspend") 632 633 if not can_hibernate: 634 for options in lid_options, button_power_options, critical_options: 635 remove(options, "hibernate") 636 637 return lid_options, button_power_options, critical_options, can_suspend, can_hybrid_sleep, can_hibernate 638 639class BrightnessSlider(SettingsWidget): 640 step = 5 641 642 def __init__(self, section, proxy, label): 643 super(BrightnessSlider, self).__init__() 644 self.set_orientation(Gtk.Orientation.VERTICAL) 645 self.set_spacing(0) 646 647 self.timer = None 648 self.section = section 649 self.proxy = proxy 650 651 hbox = Gtk.Box() 652 653 self.label = Gtk.Label.new(label) 654 self.label.set_halign(Gtk.Align.CENTER) 655 656 self.min_label= Gtk.Label() 657 self.max_label = Gtk.Label() 658 self.min_label.set_alignment(1.0, 0.75) 659 self.max_label.set_alignment(1.0, 0.75) 660 self.min_label.set_margin_right(6) 661 self.max_label.set_margin_left(6) 662 self.min_label.set_markup("<i><small>0%</small></i>") 663 self.max_label.set_markup("<i><small>100%</small></i>") 664 665 step = 5 666 667 try: 668 # Keyboard backlight 669 step = proxy.GetStep() 670 self.content_widget = Gtk.Scale.new_with_range(Gtk.Orientation.HORIZONTAL, 0, 100, step) 671 val = 0 672 673 while val < 100 - step: 674 val += step 675 676 self.content_widget.add_mark(val, Gtk.PositionType.BOTTOM, None) 677 678 except GLib.Error: 679 self.content_widget = Gtk.Scale.new_with_range(Gtk.Orientation.HORIZONTAL, 1, 100, step) 680 681 self.step = step 682 683 self.content_widget.set_draw_value(False) 684 self.content_widget.set_digits(0) 685 686 hbox.pack_start(self.min_label, False, False, 0) 687 hbox.pack_start(self.content_widget, True, True, 0) 688 hbox.pack_start(self.max_label, False, False, 0) 689 690 self.pack_start(self.label, False, False, 0) 691 self.pack_start(hbox, True, True, 6) 692 693 self.proxy.connect("g-signal", self.on_dbus_changed) 694 self.content_widget.connect("scroll-event", self.on_scroll_event) 695 self.content_widget.connect("change-value", self.on_change_value) 696 697 self.value_changed_id = self.content_widget.connect("value-changed", self.apply_later) 698 699 self.on_dbus_changed() 700 701 def apply_later(self, *args): 702 def apply(self): 703 self.proxy.SetPercentage("(u)", self.content_widget.get_value()) 704 self.timer = None 705 706 if self.timer: 707 GLib.source_remove(self.timer) 708 self.timer = GLib.timeout_add(300, apply, self) 709 710 def on_change_value(self, range, scroll_type, value, data=None): 711 # Keyboard backlights can have very few adjustment steps (for instance, 712 # 0, 50% and 100% in the case of a lenovo p51 laptop.) It's desirable 713 # to have the adjustment ratchet to these values, otherwise you can 714 # have the slider misrepresenting the actual value. 715 # 716 # This intermediate step (change-value signal) lets us clamp the new 717 # value to an actual valid position before sending it to the proxy. 718 i = 0 719 v = round(value) 720 step = self.step 721 722 while i < 100: 723 if v > i + step: 724 i += step 725 continue 726 727 if ((i + step) - v) < (v - i): 728 v = i + step 729 else: 730 v = i 731 732 break 733 734 self.content_widget.set_value(v) 735 736 return True 737 738 def on_scroll_event(self, widget, event): 739 found, delta_x, delta_y = event.get_scroll_deltas() 740 741 # If you scroll up, delta_y < 0. This is a weird world 742 self.content_widget.set_value(widget.get_value() - delta_y * self.step) 743 744 return True 745 746 def on_dbus_changed(self, *args): 747 try: 748 brightness = self.proxy.GetPercentage() 749 750 self.content_widget.handler_block(self.value_changed_id) 751 self.content_widget.set_value(brightness) 752 self.content_widget.handler_unblock(self.value_changed_id) 753 except: 754 self.section.hide() 755 756class GSettings2ComboBox(SettingsWidget): 757 def __init__(self, label, schema, key1, key2, options, valtype="string", dep_key=None, size_group=None): 758 super(GSettings2ComboBox, self).__init__(dep_key=dep_key) 759 760 self.settings = Gio.Settings.new(schema) 761 self.key1 = key1 762 self.key2 = key2 763 self.option_map = {} 764 765 self.label = Gtk.Label.new(label) 766 if valtype == "string": 767 self.model = Gtk.ListStore(str, str) 768 else: 769 self.model = Gtk.ListStore(int, str) 770 771 selected = None 772 for option in options: 773 iter = self.model.insert_before(None, None) 774 self.model.set_value(iter, 0, option[0]) 775 self.model.set_value(iter, 1, option[1]) 776 self.option_map[option[0]] = iter 777 778 self.content_widget1 = Gtk.ComboBox.new_with_model(self.model) 779 renderer_text = Gtk.CellRendererText() 780 self.content_widget1.pack_start(renderer_text, True) 781 self.content_widget1.add_attribute(renderer_text, "text", 1) 782 self.content_widget1.key = key1 783 784 self.content_widget2 = Gtk.ComboBox.new_with_model(self.model) 785 renderer_text = Gtk.CellRendererText() 786 self.content_widget2.pack_start(renderer_text, True) 787 self.content_widget2.add_attribute(renderer_text, "text", 1) 788 self.content_widget2.key = key2 789 790 self.pack_start(self.label, False, False, 0) 791 self.pack_end(self.content_widget2, False, True, 0) 792 self.pack_end(self.content_widget1, False, True, 0) 793 794 self.content_widget1.connect('changed', self.on_my_value_changed) 795 self.content_widget2.connect('changed', self.on_my_value_changed) 796 self.settings.connect("changed::" + self.key1, self.on_my_setting_changed1) 797 self.settings.connect("changed::" + self.key2, self.on_my_setting_changed2) 798 self.on_my_setting_changed1() 799 self.on_my_setting_changed2() 800 801 if size_group: 802 self.add_to_size_group(size_group) 803 804 def on_my_value_changed(self, widget): 805 tree_iter = widget.get_active_iter() 806 if tree_iter != None: 807 self.settings[widget.key] = self.model[tree_iter][0] 808 809 def on_my_setting_changed1(self, *args): 810 try: 811 self.content_widget1.set_active_iter(self.option_map[self.settings[self.key1]]) 812 except: 813 self.content_widget1.set_active_iter(None) 814 815 def on_my_setting_changed2(self, *args): 816 try: 817 self.content_widget2.set_active_iter(self.option_map[self.settings[self.key2]]) 818 except: 819 self.content_widget2.set_active_iter(None) 820 821 def add_to_size_group(self, group): 822 group.add_widget(self.content_widget1) 823 group.add_widget(self.content_widget2) 824