├── screenshot.gif ├── schemas ├── gschemas.compiled └── org.gnome.shell.extensions.turnoffdisplay.gschema.xml ├── locale ├── cs │ └── LC_MESSAGES │ │ └── turnoffdisplay.mo ├── de │ └── LC_MESSAGES │ │ └── turnoffdisplay.mo └── en │ └── LC_MESSAGES │ └── turnoffdisplay.mo ├── metadata.json ├── turnoffdisplay.pot ├── utils.js ├── en_EN.po ├── css_adjust.js ├── de_DE.po ├── icons └── disable-display-symbolic.svg ├── xinput_mouse.js ├── extension.js └── prefs.js /screenshot.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonthechipmunk/turnoffdisplay/HEAD/screenshot.gif -------------------------------------------------------------------------------- /schemas/gschemas.compiled: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonthechipmunk/turnoffdisplay/HEAD/schemas/gschemas.compiled -------------------------------------------------------------------------------- /locale/cs/LC_MESSAGES/turnoffdisplay.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonthechipmunk/turnoffdisplay/HEAD/locale/cs/LC_MESSAGES/turnoffdisplay.mo -------------------------------------------------------------------------------- /locale/de/LC_MESSAGES/turnoffdisplay.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonthechipmunk/turnoffdisplay/HEAD/locale/de/LC_MESSAGES/turnoffdisplay.mo -------------------------------------------------------------------------------- /locale/en/LC_MESSAGES/turnoffdisplay.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonthechipmunk/turnoffdisplay/HEAD/locale/en/LC_MESSAGES/turnoffdisplay.mo -------------------------------------------------------------------------------- /metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Adds a button to the status menu to turn off the screen.\n\n(requires 'xset' and 'xinput' (xorg). On wayland it instead activates the screen saver. So make sure it is configured appropriately.)", 3 | "name": "Turn off Display", 4 | "settings-schema": "org.gnome.shell.extensions.turnoffdisplay", 5 | "shell-version": [ 6 | "3.10", 7 | "3.12", 8 | "3.14", 9 | "3.16", 10 | "3.18", 11 | "3.20", 12 | "3.22", 13 | "3.24", 14 | "3.26", 15 | "3.28", 16 | "3.30" 17 | ], 18 | "url": "https://github.com/simonthechipmunk/turnoffdisplay", 19 | "uuid": "turnoffdisplay@simonthechipmunk.noreply.com", 20 | "gettext-domain": "turnoffdisplay", 21 | "version": 15 22 | } 23 | -------------------------------------------------------------------------------- /schemas/org.gnome.shell.extensions.turnoffdisplay.gschema.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | true 5 | Round Button 6 | Sets the button to either be a separate item or add it to the shutdown/lock controls 7 | 8 | 9 | false 10 | Handle Mouse 11 | Turns off external mouse pointers when screen is disabled 12 | 13 | 14 | 'auto' 15 | Menu Handling Mode 16 | The mode for handling the main menu width 17 | 18 | 19 | 450 20 | Fixed Menu Width 21 | Fixed width of the aggregated menu 22 | 23 | 24 | 25 | Keyboard Shortcut 26 | Keybinding to turn off the Display 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /turnoffdisplay.pot: -------------------------------------------------------------------------------- 1 | # Translation template for "Turn off Display". 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the extension package. 4 | # Simon Junga , 2015. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2015-10-04 15:08+0200\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | #: extension.js:181 extension.js:195 21 | msgid "Display Off" 22 | msgstr "" 23 | 24 | #: prefs.js:84 25 | msgid "Integrate into System Controls (Round Buttons)" 26 | msgstr "" 27 | 28 | #: prefs.js:85 29 | msgid "Adds a round button instead of a separate entry in the Systemmenu" 30 | msgstr "" 31 | 32 | #: prefs.js:88 prefs.js:95 prefs.js:100 33 | msgid "Disable Mouse when Screen is turned Off" 34 | msgstr "" 35 | 36 | #: prefs.js:89 37 | msgid "" 38 | "Automatically disable all external mouse pointers to prevent the screen from " 39 | "turning on accidentally." 40 | msgstr "" 41 | 42 | #: prefs.js:96 43 | msgid "Disabling mouse input automatically is not yet supported with Wayland." 44 | msgstr "" 45 | 46 | #: prefs.js:101 47 | msgid "" 48 | "Disabling mouse input automatically requires Xinput. Please install it and " 49 | "restart Gnome-Shell." 50 | msgstr "" 51 | 52 | #: prefs.js:109 53 | msgid "Menu Width adjustment Mode" 54 | msgstr "" 55 | 56 | #: prefs.js:109 57 | msgid "Select the mode for handling the width of the Main Menu." 58 | msgstr "" 59 | 60 | #: prefs.js:110 61 | msgid "Off" 62 | msgstr "" 63 | 64 | #: prefs.js:110 65 | msgid "Automatic" 66 | msgstr "" 67 | 68 | #: prefs.js:110 69 | msgid "Fixed" 70 | msgstr "" 71 | 72 | #: prefs.js:112 73 | msgid "Fixed menu size in px." 74 | msgstr "" 75 | 76 | #: prefs.js:116 77 | msgid "Custom Keybinding" 78 | msgstr "" 79 | 80 | #: prefs.js:117 81 | msgid "" 82 | "Set a Keybinding to turn off the Display (Examples: F12, space, " 83 | "w)" 84 | msgstr "" 85 | 86 | #: prefs.js:118 87 | msgid "Keybinding is enabled" 88 | msgstr "" 89 | 90 | #: prefs.js:119 91 | msgid "Keybinding is disabled. Set a valid Shortcut to enable it" 92 | msgstr "" 93 | -------------------------------------------------------------------------------- /utils.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Gnome3 extension utilities 3 | * 4 | * Basic helper functions 5 | * 6 | * 7 | * This program is free software; you can redistribute it and/or modify it 8 | * under the terms of the GNU General Public License as published by the Free 9 | * Software Foundation; either version 2 of the License, or (at your option) 10 | * any later version. 11 | * 12 | */ 13 | 14 | 15 | //***// imports: 16 | 17 | // utilities for external programs and command line 18 | const Gio = imports.gi.Gio; 19 | 20 | // translations 21 | const Config = imports.misc.config; 22 | const Gettext = imports.gettext; 23 | 24 | // own imports 25 | const Me = imports.misc.extensionUtils.getCurrentExtension(); 26 | 27 | 28 | 29 | 30 | //***// basic helper functions 31 | 32 | /* 33 | * _getSettingsSchema: 34 | * @schema: (optional): the gsettings schema to use 35 | * 36 | * Get the settings Schema for gsettings 37 | * If @schema is not provided, it will be taken from metadata['gsettings-schema'] 38 | */ 39 | function _getSettingsSchema(setschema) { 40 | 41 | const GioSSS = Gio.SettingsSchemaSource; 42 | 43 | // get the gsettings schema and path 44 | let schema = setschema || Me.metadata['settings-schema']; 45 | let source = GioSSS.new_from_directory(Me.dir.get_child('schemas').get_path(), 46 | GioSSS.get_default(), 47 | false); 48 | 49 | 50 | return new Gio.Settings({ 51 | settings_schema : source.lookup(schema, true) 52 | }); 53 | 54 | } 55 | 56 | 57 | 58 | /* 59 | * _initTranslations: 60 | * @domain: (optional): the gettext domain to use 61 | * 62 | * Initialize Gettext to load translations from extensionsdir/locale. 63 | * If @domain is not provided, it will be taken from metadata['gettext-domain'] 64 | */ 65 | function _initTranslations(setdomain) { 66 | 67 | let domain = setdomain || Me.metadata['gettext-domain']; 68 | 69 | // check if this extension was built with "make zip-file", and thus 70 | // has the locale files in a subfolder 71 | // otherwise assume that extension has been installed in the 72 | // same prefix as gnome-shell 73 | let localeDir = Me.dir.get_child('locale'); 74 | if (localeDir.query_exists(null)) { 75 | Gettext.bindtextdomain(domain, localeDir.get_path()); 76 | } 77 | 78 | else { 79 | Gettext.bindtextdomain(domain, Config.LOCALEDIR); 80 | } 81 | 82 | } 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /en_EN.po: -------------------------------------------------------------------------------- 1 | # Translation template for "Turn off Display". 2 | # Copyright (C) 2015 THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the extension package. 4 | # Simon Junga , 2015. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2015-10-04 15:08+0200\n" 12 | "PO-Revision-Date: 2015-03-28 17:48+0100\n" 13 | "Last-Translator: Simon \n" 14 | "Language-Team: English\n" 15 | "Language: en_EN\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 20 | 21 | #: extension.js:181 extension.js:195 22 | msgid "Display Off" 23 | msgstr "Display Off" 24 | 25 | #: prefs.js:84 26 | msgid "Integrate into System Controls (Round Buttons)" 27 | msgstr "Integrate into System Controls (Round Buttons)" 28 | 29 | #: prefs.js:85 30 | msgid "Adds a round button instead of a separate entry in the Systemmenu" 31 | msgstr "Adds a round button instead of a separate entry in the Systemmenu" 32 | 33 | #: prefs.js:88 prefs.js:95 prefs.js:100 34 | msgid "Disable Mouse when Screen is turned Off" 35 | msgstr "Disable Mouse when Screen is turned Off" 36 | 37 | #: prefs.js:89 38 | msgid "" 39 | "Automatically disable all external mouse pointers to prevent the screen from " 40 | "turning on accidentally." 41 | msgstr "" 42 | "Automatically disable all external mouse pointers to prevent the screen from " 43 | "turning on accidentally." 44 | 45 | #: prefs.js:96 46 | msgid "Disabling mouse input automatically is not yet supported with Wayland." 47 | msgstr "" 48 | 49 | #: prefs.js:101 50 | msgid "" 51 | "Disabling mouse input automatically requires Xinput. Please install it and " 52 | "restart Gnome-Shell." 53 | msgstr "" 54 | 55 | #: prefs.js:109 56 | msgid "Menu Width adjustment Mode" 57 | msgstr "Menu Width adjustment Mode" 58 | 59 | #: prefs.js:109 60 | msgid "Select the mode for handling the width of the Main Menu." 61 | msgstr "Select the mode for handling the width of the Main Menu." 62 | 63 | #: prefs.js:110 64 | msgid "Off" 65 | msgstr "Off" 66 | 67 | #: prefs.js:110 68 | msgid "Automatic" 69 | msgstr "Automatic" 70 | 71 | #: prefs.js:110 72 | msgid "Fixed" 73 | msgstr "Fixed" 74 | 75 | #: prefs.js:112 76 | msgid "Fixed menu size in px." 77 | msgstr "Fixed menu size in px." 78 | 79 | #: prefs.js:116 80 | msgid "Custom Keybinding" 81 | msgstr "Custom Keybinding" 82 | 83 | #: prefs.js:117 84 | msgid "" 85 | "Set a Keybinding to turn off the Display (Examples: F12, space, " 86 | "w)" 87 | msgstr "" 88 | "Set a Keybinding to turn off the Display (Examples: F12, space, " 89 | "w)" 90 | 91 | #: prefs.js:118 92 | msgid "Keybinding is enabled" 93 | msgstr "Keybinding is enabled" 94 | 95 | #: prefs.js:119 96 | msgid "Keybinding is disabled. Set a valid Shortcut to enable it" 97 | msgstr "Keybinding is disabled. Set a valid Shortcut to enable it" 98 | -------------------------------------------------------------------------------- /css_adjust.js: -------------------------------------------------------------------------------- 1 | /* 2 | * css_adjust.js 3 | * Gnome3 Turn off Display Extension 4 | * 5 | * Helper for handling automatic or manual adjustment of the aggregate menu to fit all added items in the system area. 6 | * 7 | * 8 | * Author: Simon Junga (simonthechipmunk at gmx.de) 9 | * 10 | * This program is free software; you can redistribute it and/or modify it 11 | * under the terms of the GNU General Public License as published by the Free 12 | * Software Foundation; either version 2 of the License, or (at your option) 13 | * any later version. 14 | * 15 | */ 16 | 17 | 18 | //***// imports: 19 | 20 | // main functionality 21 | const Main = imports.ui.main; 22 | 23 | 24 | 25 | 26 | // define global variables 27 | let menuChangedAdd, menuChangedRem; 28 | 29 | let aggregateMenu = Main.panel.statusArea['aggregateMenu']; 30 | let aggregateMenuWidth = aggregateMenu.menu.actor.get_width(); 31 | 32 | 33 | 34 | //***// basic functions 35 | 36 | function handle_aggregate_menu(mode, width) { 37 | // set mode 38 | 39 | if(mode == "auto" && !menuChangedAdd && !menuChangedRem) { 40 | 41 | 42 | // monitor changes in the system area 43 | menuChangedAdd = aggregateMenu._system._actionsItem.actor.connect('actor_added', function() { 44 | 45 | checkAggregatemenuwidth(); 46 | }); 47 | 48 | menuChangedRem = aggregateMenu._system._actionsItem.actor.connect('actor_removed', function() { 49 | 50 | checkAggregatemenuwidth(); 51 | }); 52 | 53 | // initial menu adjustment 54 | checkAggregatemenuwidth(); 55 | 56 | } 57 | 58 | 59 | else if(mode == "fixed" && width >= aggregateMenuWidth) { 60 | // set menu to fixed size 61 | disconnect_all(); 62 | aggregateMenu.menu.actor.set_width(width); 63 | } 64 | 65 | 66 | else if(mode == "off") { 67 | // reset menu to original size 68 | disconnect_all(); 69 | aggregateMenu.menu.actor.set_width(aggregateMenuWidth); 70 | } 71 | 72 | 73 | } 74 | 75 | 76 | 77 | 78 | 79 | function checkAggregatemenuwidth() { 80 | // run menu width adjustment if necessary 81 | 82 | 83 | let actionChildren = aggregateMenu._system._actionsItem.actor.get_children(); 84 | let allButtonWidth = actionChildren[1].get_width()*2; 85 | 86 | //calculate new width 87 | for (let button of actionChildren) { 88 | allButtonWidth += button.get_width(); 89 | } 90 | 91 | // change the menu width 92 | if(aggregateMenuWidth < allButtonWidth){ 93 | aggregateMenu.menu.actor.set_width(allButtonWidth); 94 | } 95 | else if (aggregateMenu.menu.actor.get_width() != aggregateMenuWidth){ 96 | aggregateMenu.menu.actor.set_width(aggregateMenuWidth); 97 | } 98 | 99 | } 100 | 101 | 102 | 103 | 104 | 105 | function disconnect_all() { 106 | // disconnect from active signals 107 | 108 | if(menuChangedAdd) { 109 | aggregateMenu.menu.actor.disconnect(menuChangedAdd); 110 | menuChangedAdd = null; 111 | } 112 | 113 | if(menuChangedRem) { 114 | aggregateMenu.menu.actor.disconnect(menuChangedRem); 115 | menuChangedRem = null; 116 | } 117 | } 118 | 119 | -------------------------------------------------------------------------------- /de_DE.po: -------------------------------------------------------------------------------- 1 | # Translation template for "Turn off Display". 2 | # Copyright (C) 2015 THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the extension package. 4 | # Simon Junga , 2015. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2015-10-04 15:08+0200\n" 12 | "PO-Revision-Date: 2015-03-28 17:48+0100\n" 13 | "Last-Translator: Simon \n" 14 | "Language-Team: German\n" 15 | "Language: de_DE\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 20 | 21 | #: extension.js:181 extension.js:195 22 | msgid "Display Off" 23 | msgstr "Bildschirm Aus" 24 | 25 | #: prefs.js:84 26 | msgid "Integrate into System Controls (Round Buttons)" 27 | msgstr "Im Systembereich anzeigen (Runde Schaltflächen)" 28 | 29 | #: prefs.js:85 30 | msgid "Adds a round button instead of a separate entry in the Systemmenu" 31 | msgstr "Erstellt eine runde Schaltfläche anstatt eines separaten Menüeintrags" 32 | 33 | #: prefs.js:88 prefs.js:95 prefs.js:100 34 | msgid "Disable Mouse when Screen is turned Off" 35 | msgstr "Maus deaktivieren wenn der Bildschirm aus ist" 36 | 37 | #: prefs.js:89 38 | msgid "" 39 | "Automatically disable all external mouse pointers to prevent the screen from " 40 | "turning on accidentally." 41 | msgstr "" 42 | "Automatisch alle angeschlossenen Zeigegeräte (Mäuse) deaktivieren um ein " 43 | "versehentliches Einschalten des Bildschirms zu verhindern." 44 | 45 | #: prefs.js:96 46 | msgid "Disabling mouse input automatically is not yet supported with Wayland." 47 | msgstr "Automatisches Abschalten angeschlossener Zeigegeräte (Mäuse) wird unter Wayland zur Zeit nicht unterstützt." 48 | 49 | #: prefs.js:101 50 | msgid "" 51 | "Disabling mouse input automatically requires Xinput. Please install it and " 52 | "restart Gnome-Shell." 53 | msgstr "Zum automatischen Abschalten angeschlossener Zeigegeräte (Mäuse) wird Xinput benötigt. Bitte installieren Sie es und starten Gnome-Shell neu." 54 | 55 | #: prefs.js:109 56 | msgid "Menu Width adjustment Mode" 57 | msgstr "Anpassung der Menübreite" 58 | 59 | #: prefs.js:109 60 | msgid "Select the mode for handling the width of the Main Menu." 61 | msgstr "Modusauswahl zur Breitenanpassung des Hauptmenüs." 62 | 63 | #: prefs.js:110 64 | msgid "Off" 65 | msgstr "Aus" 66 | 67 | #: prefs.js:110 68 | msgid "Automatic" 69 | msgstr "Automatisch" 70 | 71 | #: prefs.js:110 72 | msgid "Fixed" 73 | msgstr "Manuell" 74 | 75 | #: prefs.js:112 76 | msgid "Fixed menu size in px." 77 | msgstr "Feste Menübreite in px." 78 | 79 | #: prefs.js:116 80 | msgid "Custom Keybinding" 81 | msgstr "Eigene Tastenkombination" 82 | 83 | #: prefs.js:117 84 | msgid "" 85 | "Set a Keybinding to turn off the Display (Examples: F12, space, " 86 | "w)" 87 | msgstr "" 88 | "Tastenkürzel um den Bildschirm auszuschalten (Beispiele: F12, space, " 89 | "w)" 90 | 91 | #: prefs.js:118 92 | msgid "Keybinding is enabled" 93 | msgstr "Tastenkürzel ist aktiv" 94 | 95 | #: prefs.js:119 96 | msgid "Keybinding is disabled. Set a valid Shortcut to enable it" 97 | msgstr "" 98 | "Tastenkürzel ist inaktiv. Zum Aktivieren gültige Tastenkombination eingeben" 99 | -------------------------------------------------------------------------------- /icons/disable-display-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 41 | Gnome Symbolic Icon Theme 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | Gnome Symbolic Icon Theme 52 | 53 | 54 | 55 | 58 | 62 | 66 | 67 | 72 | 77 | 78 | -------------------------------------------------------------------------------- /xinput_mouse.js: -------------------------------------------------------------------------------- 1 | /* 2 | * xinput_mouse.js 3 | * Gnome3 Turn off Display Extension 4 | * 5 | * Helper for Xinput to detect and disable mouse input devices. Adapted from Touchpad Indicator by https://extensions.gnome.org/accounts/profile/orangeshirt 6 | * 7 | * 8 | * Author: Simon Junga (simonthechipmunk at gmx.de) 9 | * Original Author: Armin Köhler (orangeshirt at web.de>) 10 | * 11 | * This program is free software; you can redistribute it and/or modify it 12 | * under the terms of the GNU General Public License as published by the Free 13 | * Software Foundation; either version 2 of the License, or (at your option) 14 | * any later version. 15 | * 16 | */ 17 | 18 | 19 | //***// imports: 20 | 21 | // utilities for external programs and command line 22 | const GLib = imports.gi.GLib; 23 | 24 | 25 | 26 | 27 | // define global variables 28 | let xinput_is_installed = xinput_installed(); 29 | 30 | const exclude_devices = new Array('touchpad','glidepoint','fingersensingpad', 'bcm5974','trackpad','smartpad', 31 | 'trackpoint','accu point','trackstick', 'touchstyk','pointing stick','dualpoint stick', 32 | 'touchscreen', 'maxtouch', 'touch', 33 | 'pen stylus', 'pen eraser'); 34 | 35 | 36 | 37 | 38 | //***// basic functions 39 | 40 | function get_pointer_ids() { 41 | // get all IDs for devices that match the property "pointing device" 42 | let pointerIDs = new Array(); 43 | let lines = execute_sync('xinput --list'); 44 | if (lines) { 45 | lines = lines[1].toString().split('\n'); 46 | let y = 0; 47 | for (let line = 0; line < lines.length; line++) { 48 | if ((lines[line].indexOf('slave pointer') != -1) && (lines[line].indexOf('XTEST') == -1)) { 49 | pointerIDs[y] = lines[line].toString().split('=')[1].split('[')[0].split('\t')[0]; 50 | y++; 51 | } 52 | } 53 | } 54 | return pointerIDs; 55 | 56 | } 57 | 58 | 59 | 60 | 61 | 62 | function get_mouse_ids() { 63 | // get all IDs for devices that match the property "pointing device" and are not listed in exclude_devices 64 | let mouseIDs = new Array(); 65 | let lines = execute_sync('xinput --list'); 66 | if (lines) { 67 | lines = lines[1].toString().split('\n'); 68 | let y = 0; 69 | for (let line = 0; line < lines.length; line++) { 70 | if ((lines[line].indexOf('slave pointer') !=- 1) && (lines[line].indexOf('XTEST') == -1)) { 71 | // check for devices on exclude list 72 | let checkOK = 1; 73 | for (let check = 0; check < exclude_devices.length; check++) { 74 | if ((lines[line].toLowerCase().indexOf(exclude_devices[check].toString().toLowerCase()) ) != -1) { 75 | checkOK = 0; 76 | } 77 | } 78 | 79 | if(checkOK) { 80 | mouseIDs[y] = lines[line].toString().split('=')[1].split('[')[0].split('\t')[0]; 81 | y++; 82 | } 83 | } 84 | } 85 | } 86 | return mouseIDs; 87 | 88 | } 89 | 90 | 91 | 92 | 93 | 94 | function switch_devices(mode, deviceIDs) { 95 | // enable/disable devices by ID 96 | for (let device of deviceIDs) { 97 | if(mode == "on") { 98 | execute_sync('xinput --enable ' + device); 99 | } 100 | 101 | else if(mode == "off") { 102 | execute_sync('xinput --disable ' + device); 103 | } 104 | } 105 | 106 | } 107 | 108 | 109 | 110 | 111 | function execute_sync(command) { 112 | //run a command on the command line 113 | try { 114 | return GLib.spawn_command_line_sync(command); 115 | } 116 | catch (err) { 117 | return false; 118 | } 119 | } 120 | 121 | 122 | 123 | 124 | function xinput_installed(){ 125 | //check if xinput is present 126 | if(execute_sync('xinput --list')){ 127 | return true; 128 | } 129 | return false; 130 | } 131 | 132 | -------------------------------------------------------------------------------- /extension.js: -------------------------------------------------------------------------------- 1 | /* 2 | * extension.js 3 | * Gnome3 Turn off Display Extension 4 | * 5 | * Adds a button to the status menu to turn off the screen. This extension is here to continue "Blank Screen" by l300lvl which is updated to GS3.6 6 | * https://extensions.gnome.org/extension/242/blank-screen/ *** https://github.com/l300lvl/Blank-Screen-Extension 7 | * 8 | * 9 | * Author: Simon Junga (simonthechipmunk at gmx.de) 10 | * Original Author: l300lvl 11 | * 12 | * This program is free software; you can redistribute it and/or modify it 13 | * under the terms of the GNU General Public License as published by the Free 14 | * Software Foundation; either version 2 of the License, or (at your option) 15 | * any later version. 16 | * 17 | */ 18 | 19 | 20 | //***// imports: 21 | 22 | // icons and labels 23 | const St = imports.gi.St; 24 | 25 | // main functionality 26 | const Main = imports.ui.main; 27 | const Meta = imports.gi.Meta; 28 | const Shell = imports.gi.Shell; 29 | const Mainloop = imports.mainloop; 30 | 31 | // menu items 32 | const PopupMenu = imports.ui.popupMenu; 33 | 34 | // utilities for external programs and command line 35 | const Config = imports.misc.config; 36 | const ShellVersion = Config.PACKAGE_VERSION.split('.'); 37 | const Util = imports.misc.util; 38 | const GLib = imports.gi.GLib; 39 | 40 | // clutter and Gtk 41 | const Gtk = imports.gi.Gtk; 42 | 43 | // translations 44 | const Gettext = imports.gettext.domain('turnoffdisplay'); 45 | const _ = Gettext.gettext; 46 | 47 | // own imports 48 | const Me = imports.misc.extensionUtils.getCurrentExtension(); 49 | const Prefs = Me.imports.prefs; 50 | const Utils = Me.imports.utils; 51 | const CSSadjust = Me.imports.css_adjust; 52 | const Xinput = Me.imports.xinput_mouse; 53 | const Settings = Utils._getSettingsSchema(); 54 | 55 | 56 | 57 | 58 | // define global variables 59 | let menuitem, button, systemMenu, menuSettings, keybindSettings, handlemenumodeSettings, fixmenuwidthSettings; 60 | let eventKeybind=null; 61 | let eventXsetwatch=null; 62 | let eventStartup=null; 63 | 64 | 65 | 66 | //***// basic extension functions 67 | 68 | function init() { 69 | // initialize preferences 70 | Prefs.init(); 71 | 72 | // get extension icons 73 | Gtk.IconTheme.get_default().append_search_path(Me.dir.get_child('icons').get_path()); 74 | } 75 | 76 | 77 | 78 | 79 | function enable() { 80 | 81 | // create the menu entry 82 | _MenuEntry(true); 83 | 84 | // set keybinding 85 | _SetKeybinding(true); 86 | 87 | // start menu width adjustment 88 | CSSadjust.handle_aggregate_menu(Prefs._getHandleMenuMode(), Prefs._getFixMenuWidth() ); 89 | 90 | 91 | 92 | // TODO:BUG something is really screwed up here... the get_width() method of any system-area button will return wrong values until 93 | // the shell is running for at least a few seconds. For now calling the adjustment method 5 seconds into operation is the only thing 94 | // that actually helps. Since I'm already 2 weeks late for the 3.20 release because of that this shit is now going upstream...*sigh* 95 | //eventStartup = GLib.idle_add(GLib.PRIORITY_LOW, function() { 96 | eventStartup = GLib.timeout_add(0, 5000, function() { 97 | 98 | if(Prefs._getHandleMenuMode() == "auto" && ShellVersion[1] <= 16) CSSadjust.checkAggregatemenuwidth(); 99 | return false; 100 | }); 101 | 102 | 103 | // connect to signals "preference changed" 104 | menuSettings = Settings.connect('changed::integrate', function() { 105 | _MenuEntry(false); 106 | _MenuEntry(true); 107 | }); 108 | 109 | keybindSettings = Settings.connect('changed::turnoffdisplay-keybinding', function() { 110 | _SetKeybinding(false); 111 | _SetKeybinding(true); 112 | }); 113 | 114 | handlemenumodeSettings = Settings.connect('changed::handlemenumode', function() { 115 | CSSadjust.handle_aggregate_menu(Prefs._getHandleMenuMode(), Prefs._getFixMenuWidth() ); 116 | }); 117 | 118 | fixmenuwidthSettings = Settings.connect('changed::fixmenuwidth', function() { 119 | if(Prefs._getHandleMenuMode() == "fixed") { 120 | CSSadjust.handle_aggregate_menu("fixed", Prefs._getFixMenuWidth() ); 121 | } 122 | }); 123 | 124 | } 125 | 126 | 127 | 128 | 129 | function disable() { 130 | // disable the menu entry 131 | _MenuEntry(false); 132 | 133 | // remove keybinding 134 | _SetKeybinding(false); 135 | 136 | // disconnect from signals "preference changed" 137 | Settings.disconnect(menuSettings); 138 | Settings.disconnect(keybindSettings); 139 | Settings.disconnect(handlemenumodeSettings); 140 | Settings.disconnect(fixmenuwidthSettings); 141 | 142 | // remove timer event 143 | if(eventKeybind) { 144 | Mainloop.source_remove(eventKeybind); 145 | } 146 | 147 | if(eventXsetwatch) { 148 | Mainloop.source_remove(eventXsetwatch); 149 | } 150 | 151 | if(eventStartup) { 152 | Mainloop.source_remove(eventStartup); 153 | } 154 | 155 | // disable menu width adjustment 156 | CSSadjust.handle_aggregate_menu("off"); 157 | 158 | } 159 | 160 | 161 | 162 | 163 | 164 | 165 | //***// extension functions 166 | 167 | function _MenuEntry(set) { 168 | // create/destroy the menu entry 169 | 170 | // enable the entry 171 | if(set) { 172 | 173 | // create the menu entry according to preference settings 174 | if(!Prefs._getIntegrate()) { 175 | 176 | systemMenu = Main.panel.statusArea['aggregateMenu']; 177 | 178 | // create separate menu button 179 | menuitem = new PopupMenu.PopupBaseMenuItem({ activate: true }); 180 | let icon = new St.Icon({ icon_name: 'disable-display-symbolic', style_class: 'popup-menu-icon' }); 181 | let text = new St.Label({ text: _("Display Off"), style_class: "sm-label" }); 182 | menuitem.actor.add(icon); 183 | menuitem.actor.add(text); 184 | menuitem.connect('activate', _DisplayOff); 185 | // add the menuentry to the menu 186 | systemMenu.menu.addMenuItem(menuitem, 0); 187 | 188 | } 189 | 190 | else { 191 | 192 | systemMenu = Main.panel.statusArea['aggregateMenu']._system; 193 | 194 | // create round button in system control area 195 | button = systemMenu._createActionButton('disable-display-symbolic', _("Display Off")); 196 | button.connect('clicked', _DisplayOff); 197 | // add the menuentry to the menu 198 | //systemMenu._actionsItem.actor.insert_child_at_index(button, 4); 199 | systemMenu._actionsItem.actor.add(button, { expand: true, x_fill: false }); 200 | } 201 | 202 | } 203 | 204 | 205 | // disable the entry 206 | else { 207 | 208 | if(menuitem) { 209 | // remove the menuitem 210 | menuitem.destroy(); 211 | } 212 | 213 | else { 214 | 215 | // remove the button 216 | systemMenu._actionsItem.actor.remove_child(button); 217 | } 218 | 219 | // reset menuitem/button variable 220 | menuitem = null; 221 | button = null; 222 | 223 | } 224 | 225 | } 226 | 227 | 228 | 229 | 230 | 231 | 232 | function _DisplayOff() { 233 | // turn off the display 234 | 235 | //close the menu 236 | systemMenu.menu.itemActivated(); 237 | if (GLib.getenv('XDG_SESSION_TYPE') == 'wayland') { 238 | _DisplayOffWayland(); 239 | } else { 240 | _DisplayOffXWindows(); 241 | 242 | // disable external mice if set in the preferences 243 | if(Prefs._getHandleMouse() && Xinput.xinput_is_installed) { 244 | disable_mouse(); 245 | } 246 | } 247 | 248 | } 249 | 250 | function _DisplayOffXWindows() { 251 | //use xset to disable the screen 252 | Util.spawn(['xset','dpms','force','off']); 253 | } 254 | 255 | function _DisplayOffWayland() { 256 | Util.spawn(['dbus-send', '--session', '--dest=org.gnome.ScreenSaver', 257 | '--type=method_call', '/org/gnome/ScreenSaver', 258 | 'org.gnome.ScreenSaver.SetActive', 'boolean:true']); 259 | } 260 | 261 | 262 | 263 | 264 | 265 | 266 | function _SetKeybinding(set) { 267 | // enable keybinding to turn off the display 268 | 269 | if (Prefs._getKeybinding() != "" && set) { 270 | 271 | // Shell version management 272 | let mode; 273 | 274 | if (ShellVersion[1] <= 14 ) { 275 | mode = Shell.KeyBindingMode.NORMAL; 276 | } 277 | else{ 278 | mode = Shell.ActionMode.NORMAL; 279 | } 280 | 281 | 282 | Main.wm.addKeybinding('turnoffdisplay-keybinding', Settings, Meta.KeyBindingFlags.NONE, mode, function() { 283 | // turn off display after 500ms (workaround! - needs something like 'key-release-event') 284 | eventKeybind = GLib.timeout_add(0, 500, _DisplayOff); 285 | }, null, null); 286 | 287 | } 288 | 289 | else { 290 | Main.wm.removeKeybinding('turnoffdisplay-keybinding'); 291 | } 292 | 293 | } 294 | 295 | 296 | 297 | 298 | 299 | function disable_mouse() { 300 | // disable mouse pointers and watch for display revoke 301 | 302 | let mousepointerIDs = Xinput.get_mouse_ids(); 303 | Xinput.switch_devices("off", mousepointerIDs); 304 | 305 | // check monitor status periodically 306 | eventXsetwatch = GLib.timeout_add(0, 500, function() { 307 | 308 | let lines = GLib.spawn_command_line_sync('xset -q'); 309 | if (lines) { 310 | 311 | // check for "Monitor is On" 312 | lines = lines.toString(); 313 | if (lines.indexOf('Monitor is On') != -1) { 314 | Xinput.switch_devices("on", mousepointerIDs); 315 | return false; 316 | } 317 | } 318 | 319 | return true; 320 | 321 | }); 322 | 323 | } 324 | 325 | -------------------------------------------------------------------------------- /prefs.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Preferences for the extension which will be available in the 3 | * "gnome-shell-extension-prefs" tool. 4 | * 5 | * 6 | * see: https://live.gnome.org/GnomeShell/Extensions#Extension_Preferences 7 | * 8 | */ 9 | 10 | //***// imports: 11 | 12 | // main 13 | const Gio = imports.gi.Gio; 14 | const Gtk = imports.gi.Gtk; 15 | const Lang = imports.lang; 16 | 17 | // utilities for external programs and command line 18 | const GLib = imports.gi.GLib; 19 | 20 | // translations 21 | const Gettext = imports.gettext.domain('turnoffdisplay'); 22 | const _ = Gettext.gettext; 23 | 24 | // own imports 25 | const Me = imports.misc.extensionUtils.getCurrentExtension(); 26 | const Utils = Me.imports.utils; 27 | const Xinput = Me.imports.xinput_mouse; 28 | 29 | 30 | 31 | 32 | // define global variables 33 | let settings = {}; 34 | 35 | 36 | 37 | 38 | 39 | //***// basic preferences functions 40 | 41 | function init() { 42 | 43 | // init translations 44 | Utils._initTranslations(); 45 | 46 | // load the settings schema 47 | settings = Utils._getSettingsSchema(); 48 | 49 | // create custom command bindings for gsettings "bool", "string", "stringarray" and "int" 50 | let set_boolean = Lang.bind(settings, settings.set_boolean); 51 | let set_string = Lang.bind(settings, settings.set_string); 52 | let set_strv = Lang.bind(settings, settings.set_strv); 53 | let set_int = Lang.bind(settings, settings.set_int); 54 | 55 | settings.set_boolean = function(key, value) { 56 | set_boolean(key, value); 57 | Gio.Settings.sync(); 58 | }; 59 | 60 | settings.set_string = function(key, value) { 61 | set_string(key, value); 62 | Gio.Settings.sync(); 63 | }; 64 | 65 | settings.set_strv = function(key, value) { 66 | set_strv(key, value); 67 | Gio.Settings.sync(); 68 | }; 69 | 70 | settings.set_int = function(key, value) { 71 | set_int(key, value); 72 | Gio.Settings.sync(); 73 | }; 74 | 75 | } 76 | 77 | 78 | 79 | function buildPrefsWidget() { 80 | // build the Gtk preferences widget 81 | let frame = new Gtk.Box({orientation: Gtk.Orientation.VERTICAL, border_width: 10, margin: 20}); 82 | 83 | // add items to the widget frame 84 | frame.add( _createSwitchbox( _("Integrate into System Controls (Round Buttons)"), 85 | _("Adds a round button instead of a separate entry in the Systemmenu"), _getIntegrate, _setIntegrate )); 86 | 87 | let box; 88 | let text = _("Disable Mouse when Screen is turned Off"); 89 | let tooltip = _("Automatically disable all external mouse pointers to prevent the screen from turning on accidentally."); 90 | let sensitive = true; 91 | 92 | 93 | 94 | if(GLib.getenv('XDG_SESSION_TYPE') == 'wayland'){ 95 | text = _("Disable Mouse when Screen is turned Off"); 96 | tooltip = _("Disabling mouse input automatically is not yet supported with Wayland."); 97 | sensitive = false; 98 | } 99 | else if(!Xinput.xinput_is_installed){ 100 | text = _("Disable Mouse when Screen is turned Off"); 101 | tooltip = _("Disabling mouse input automatically requires Xinput. Please install it and restart Gnome-Shell."); 102 | sensitive = false; 103 | } 104 | 105 | box = _createSwitchbox( text, tooltip, _getHandleMouse, _setHandleMouse ); 106 | box.set_sensitive(sensitive); 107 | frame.add(box); 108 | 109 | frame.add( _createComboBox( _("Menu Width adjustment Mode"), _("Select the mode for handling the width of the Main Menu."), 110 | {'off': _("Off"), 'auto' : _("Automatic"), 'fixed' : _("Fixed")}, _getHandleMenuMode, _setHandleMenuMode )); 111 | 112 | frame.add( _createAdjustBox( "", _("Fixed menu size in px."), 113 | 360.0, 1200.0, 1.0, _getFixMenuWidth, _setFixMenuWidth )); 114 | 115 | 116 | frame.add( _createKeybindbox( _("Custom Keybinding"), 117 | _("Set a Keybinding to turn off the Display (Examples: F12, space, w)"), 118 | "emblem-ok-symbolic", _("Keybinding is enabled"), 119 | "window-close-symbolic", _("Keybinding is disabled. Set a valid Shortcut to enable it"), _getKeybinding, _setKeybinding )); 120 | frame.show_all(); 121 | return frame; 122 | } 123 | 124 | 125 | 126 | 127 | 128 | 129 | //***// preferences functions 130 | 131 | function _createSwitchbox(text, tooltip, getFunction, setFunction) { 132 | // create box with toggle switch 133 | let box = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, margin: 5}); 134 | let label = new Gtk.Label({ label: text, xalign: 0, tooltip_text: tooltip }); 135 | let toggleswitch = new Gtk.Switch({ active: getFunction(), tooltip_text: tooltip }); 136 | 137 | // connect to "toggled" emit signal 138 | toggleswitch.connect('notify::active', setFunction); 139 | 140 | //fill the box with content 141 | box.pack_start(label, true, true, 0); 142 | box.add(toggleswitch); 143 | 144 | return box; 145 | } 146 | 147 | 148 | 149 | 150 | function _createComboBox(text, tooltip, values, getFunction, setFunction) { 151 | // create box with combo selection field 152 | let box = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, margin: 5}); 153 | let label = new Gtk.Label({ label: text, xalign: 0, tooltip_text: tooltip }); 154 | let widget = new Gtk.ComboBoxText({ tooltip_text: tooltip }); 155 | for (id in values) { 156 | widget.append(id, values[id]); 157 | } 158 | widget.set_active_id(getFunction()); 159 | 160 | // connect to "changed" emit signal 161 | widget.connect('changed', function(combo_widget) { 162 | setFunction(combo_widget.get_active_id()); 163 | }); 164 | //fill the box with content 165 | box.pack_start(label, true, true, 0); 166 | box.add(widget); 167 | return box; 168 | } 169 | 170 | 171 | 172 | 173 | function _createAdjustBox(text, tooltip, LOWER, UPPER, INCREM, getFunction, setFunction) { 174 | // create box with adjust selection field 175 | let box = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, margin: 5}); 176 | let label = new Gtk.Label({ label: text, xalign: 0, tooltip_text: tooltip }); 177 | let adjustbox = new Gtk.Adjustment({ lower: LOWER, step_increment: INCREM, upper: UPPER, value: 1.0 }); 178 | let spinbutton = new Gtk.SpinButton({ adjustment: adjustbox, digits: 0, tooltip_text: tooltip}); 179 | spinbutton.set_value(getFunction() ); 180 | 181 | // toggle sensitive property 182 | if(_getHandleMenuMode() != "fixed") { box.sensitive = false; } 183 | let sensitive = settings.connect('changed::handlemenumode', function() { 184 | if(_getHandleMenuMode() == "fixed") { 185 | box.sensitive = true; 186 | } 187 | else { 188 | box.sensitive = false; 189 | } 190 | }); 191 | 192 | // connect to "value-changed" emit signal 193 | spinbutton.connect('value_changed', function() { 194 | setFunction(spinbutton.value); 195 | }); 196 | //fill the box with content 197 | box.pack_start(label, true, true, 0); 198 | box.add(spinbutton, true, true, 5); 199 | 200 | return box; 201 | } 202 | 203 | 204 | 205 | 206 | function _createKeybindbox(text, tooltip, secIconOK, secIcontooltipOK, secIconNOK, secIcontooltipNOK, getFunction, setFunction) { 207 | // create box with keybinding entry 208 | let box = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, margin: 5}); 209 | let label = new Gtk.Label({ label: text, xalign: 0, tooltip_text: tooltip }); 210 | let textbox = new Gtk.Entry({ text : getFunction(), tooltip_text: tooltip }); 211 | updateTextbox(); 212 | // connect to "text-changed" emit signal and check for valid keybinding 213 | textbox.connect('changed', function() { updateTextbox(); }); 214 | 215 | function updateTextbox(){ 216 | let [key, mods] = Gtk.accelerator_parse(textbox.get_text()); 217 | 218 | if(Gtk.accelerator_valid(key, mods)) { 219 | textbox["secondary-icon-name"] = secIconOK; 220 | textbox["secondary-icon-tooltip-text"] = secIcontooltipOK; 221 | let shortcut = Gtk.accelerator_name(key, mods); 222 | _setKeybinding(shortcut); 223 | } 224 | 225 | else { 226 | textbox["secondary-icon-name"] = secIconNOK; 227 | textbox["secondary-icon-tooltip-text"] = secIcontooltipNOK; 228 | setFunction(""); 229 | } 230 | } 231 | 232 | 233 | //fill the box with content 234 | box.pack_start(label, true, true, 0); 235 | box.add(textbox, true, true, 5); 236 | 237 | return box; 238 | } 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | // functions to get/set gsettings entries 247 | function _getIntegrate() { 248 | return settings.get_boolean('integrate'); 249 | } 250 | 251 | function _setIntegrate() { 252 | settings.set_boolean('integrate', !_getIntegrate()); 253 | } 254 | 255 | 256 | function _getHandleMouse() { 257 | return settings.get_boolean('handlemouse'); 258 | } 259 | 260 | function _setHandleMouse() { 261 | settings.set_boolean('handlemouse', !_getHandleMouse()); 262 | } 263 | 264 | 265 | function _getHandleMenuMode() { 266 | return settings.get_string('handlemenumode'); 267 | } 268 | 269 | function _setHandleMenuMode(command) { 270 | settings.set_string('handlemenumode', command); 271 | } 272 | 273 | 274 | function _getFixMenuWidth() { 275 | return settings.get_int('fixmenuwidth'); 276 | } 277 | 278 | function _setFixMenuWidth(command) { 279 | settings.set_int('fixmenuwidth', command); 280 | } 281 | 282 | 283 | function _getKeybinding() { 284 | return settings.get_strv('turnoffdisplay-keybinding')[0]; 285 | } 286 | 287 | function _setKeybinding(command) { 288 | settings.set_strv('turnoffdisplay-keybinding', [command]); 289 | } 290 | 291 | --------------------------------------------------------------------------------