/* Copyright (C) 2013 Borsato Ivano The JavaScript code in this page is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License (GNU GPL) as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The code is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU GPL for more details. */ 'use strict'; import GObject from 'gi://GObject'; import St from 'gi://St'; import Meta from 'gi://Meta'; import Shell from 'gi://Shell'; // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/panelMenu.js import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js'; import Clutter from 'gi://Clutter'; import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; import * as Slider from 'resource:///org/gnome/shell/ui/slider.js'; import * as Main from 'resource:///org/gnome/shell/ui/main.js'; import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; import * as Lib from './convenience.js'; import * as Settings from './settings.js'; import * as Time from './timer.js'; import * as UtilRecorder from './utilrecorder.js'; import * as UtilAudio from './utilaudio.js'; import * as UtilWebcam from './utilwebcam.js'; import * as UtilNotify from './utilnotify.js'; import * as Selection from './selection.js'; import * as UtilExeCmd from './utilexecmd.js'; var Indicator; let timerD = null; let timerC = null; let isActive = false; let pathFile = ''; let keybindingConfigured = false; /** * @type {EasyScreenCastIndicator} */ const EasyScreenCastIndicator = GObject.registerClass({ GTypeName: 'EasyScreenCast_Indicator', }, class EasyScreenCastIndicator extends PanelMenu.Button { constructor(extension) { super(null, 'EasyScreenCast_Indicator'); this._extension = extension; this._settings = new Settings.Settings(this._extension.getSettings()); Lib.setDebugEnabled(this._settings.getOption('b', Settings.VERBOSE_DEBUG_SETTING_KEY)); this._settings._settings.connect( `changed::${Settings.VERBOSE_DEBUG_SETTING_KEY}`, () => { Lib.setDebugEnabled(this._settings.getOption('b', Settings.VERBOSE_DEBUG_SETTING_KEY)); } ); this.CtrlAudio = new UtilAudio.MixerAudio(); this.CtrlWebcam = new UtilWebcam.HelperWebcam(_('Unspecified webcam')); this.CtrlNotify = new UtilNotify.NotifyManager(); this.CtrlExe = new UtilExeCmd.ExecuteStuff(this); // load indicator icons this._icons = { on: Lib.loadIcon(this._extension.dir, 'icon_recording.svg'), onSel: Lib.loadIcon(this._extension.dir, 'icon_recordingSel.svg'), off: Lib.loadIcon(this._extension.dir, 'icon_default.svg'), offSel: Lib.loadIcon(this._extension.dir, 'icon_defaultSel.svg'), }; // check audio if (!this.CtrlAudio.checkAudio()) { Lib.TalkativeLog('-*-disable audio recording'); this._settings.setOption(Settings.INPUT_AUDIO_SOURCE_SETTING_KEY, 0); this._settings.setOption( Settings.ACTIVE_CUSTOM_GSP_SETTING_KEY, Settings.getGSPstd(false) ); } // add enter/leave/click event this.connect('enter_event', () => this.refreshIndicator(true)); this.connect('leave_event', () => this.refreshIndicator(false)); this.connect('button_press_event', (actor, event) => this._onButtonPress(actor, event) ); // prepare setting var if (this._settings.getOption('i', Settings.TIME_DELAY_SETTING_KEY) > 0) this.isDelayActive = true; else this.isDelayActive = false; // Add the title bar icon and label for time display this.indicatorBox = new St.BoxLayout(); this.indicatorIcon = new St.Icon({ gicon: this._icons.off, icon_size: 16, }); this.timeLabel = new St.Label({ text: '', style_class: 'time-label', y_expand: true, y_align: Clutter.ActorAlign.CENTER, }); this.indicatorBox.add_child(this.timeLabel); this.indicatorBox.add_child(this.indicatorIcon); // init var this.recorder = new UtilRecorder.CaptureVideo(); this.AreaSelected = null; this.TimeSlider = null; this._initMainMenu(); } /** * @private */ _initMainMenu() { this._addStartStopMenuEntry(); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.sub_menu_audio_recording = new PopupMenu.PopupSubMenuMenuItem(_('No audio source'), true); this.sub_menu_audio_recording.icon.icon_name = 'audio-input-microphone-symbolic'; this.menu.addMenuItem(this.sub_menu_audio_recording); this._addAudioRecordingSubMenu(); // add sub menu webcam recording this._addSubMenuWebCam(); this._addAreaRecordingSubMenu(); this._addRecordingDelaySubMenu(); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.menu_item_options = new PopupMenu.PopupMenuItem(_('Options')); this.menu_item_options.actor.insert_child_at_index( new St.Icon({ style_class: 'popup-menu-icon', icon_name: 'preferences-other-symbolic', }), 1 ); this.menu.addMenuItem(this.menu_item_options); this.menu_item_options.connect('activate', () => this._openExtensionPreferences()); } /** * Set a new value for the time label. Integers are * converted to seconds, minutes, hours. All other * values are converted to strings. * * @param {string|number} newValue new value of the label. if a number, then it's the seconds passed. * @returns {string} */ updateTimeLabel(newValue) { /** * @param {number} number a number */ function padZeros(number) { if (number < 10) number = `0${number}`; return number.toString(); } if (typeof newValue === 'number') { let hours = Math.floor(newValue / 3600); newValue -= hours * 3600; let minutes = Math.floor(newValue / 60); newValue -= minutes * 60; newValue = `${padZeros(hours)}:${padZeros(minutes)}:${padZeros(newValue)}`; } this.timeLabel.set_text(newValue.toString()); } /** * Left clicking on the icon toggles the recording * options menu. Any other mouse button will start * the recording. * Some submenus are refreshed to account for new * sources. * * @param {Clutter.Actor} actor the actor * @param {Clutter.Event} event a Clutter.Event */ _onButtonPress(actor, event) { let button = event.get_button(); if (button === 1) { Lib.TalkativeLog('-*-left click indicator'); this._setupExtensionMenu(); } else { Lib.TalkativeLog('-*-right click indicator'); if (this.menu.isOpen) this.menu.close(); this.isShowNotify = this._settings.getOption('b', Settings.SHOW_NOTIFY_ALERT_SETTING_KEY); this._doRecording(); } } /** * Sets up the menu when the user opens it. */ _setupExtensionMenu() { this._addAudioRecordingSubMenu(); this._addWebcamSubMenu(); } /** * Sets up all the options for web-cams. Should only run the * first time the icon is clicked an the CtrlWebcam is still * null. */ _addWebcamSubMenu() { if (this.CtrlWebcam === null) this.CtrlWebcam = new UtilWebcam.HelperWebcam(_('Unspecified webcam')); // add sub menu webcam recording this._populateSubMenuWebcam(); // start monitoring inputvideo this.CtrlWebcam.startMonitor(); } /** * Adds individual webcam items to the webcam menu. */ _populateSubMenuWebcam() { let arrMI = this._createMIWebCam(); this.smWebCam.menu.removeAll(); for (let element in arrMI) this.smWebCam.menu.addMenuItem(arrMI[element]); let i = this._settings.getOption('i', Settings.DEVICE_INDEX_WEBCAM_SETTING_KEY); Lib.TalkativeLog(`-*-populated submenuwebcam. Settings i=${i}`); this.smWebCam.label.text = this.WebCamDevice[i]; } /** * @private */ _addStartStopMenuEntry() { this.imRecordAction = new PopupMenu.PopupBaseMenuItem(); this.RecordingLabel = new St.Label({ text: _('Start recording'), style_class: 'RecordAction-label', content_gravity: Clutter.ContentGravity.CENTER, x_expand: true, x_align: Clutter.ActorAlign.CENTER, }); this.imRecordAction.actor.add_child(this.RecordingLabel); this.imRecordAction.x_expand = true; this.imRecordAction.x_fill = true; this.imRecordAction.x_align = Clutter.ActorAlign.CENTER; this.imRecordAction.connect('activate', () => { this.isShowNotify = this._settings.getOption('b', Settings.SHOW_NOTIFY_ALERT_SETTING_KEY); this._doRecording(); }); this.menu.addMenuItem(this.imRecordAction); } /** * Refreshes the submenu for audio recording sources. */ _addAudioRecordingSubMenu() { Lib.TalkativeLog('-*-reset the sub menu audio'); // remove old menu items this.sub_menu_audio_recording.menu.removeAll(); Lib.TalkativeLog('-*-add new items to sub menu audio'); var arrMI = this._createMIAudioRec(); for (var ele in arrMI) this.sub_menu_audio_recording.menu.addMenuItem(arrMI[ele]); } /** * @private */ _addSubMenuWebCam() { this.smWebCam = new PopupMenu.PopupSubMenuMenuItem('', true); this.smWebCam.icon.icon_name = 'camera-web-symbolic'; this.menu.addMenuItem(this.smWebCam); } /** * @private */ _addAreaRecordingSubMenu() { this.sub_menu_area_recording = new PopupMenu.PopupSubMenuMenuItem('', true); this.sub_menu_area_recording.icon.icon_name = 'view-fullscreen-symbolic'; var arrMI = this._createMIAreaRec(); for (var ele in arrMI) this.sub_menu_area_recording.menu.addMenuItem(arrMI[ele]); this.sub_menu_area_recording.label.text = this.AreaType[this._settings.getOption('i', Settings.AREA_SCREEN_SETTING_KEY)]; this.menu.addMenuItem(this.sub_menu_area_recording); } /** * @private */ _addRecordingDelaySubMenu() { this.smDelayRec = new PopupMenu.PopupSubMenuMenuItem('', true); this.smDelayRec.icon.icon_name = 'alarm-symbolic'; var arrMI = this._createMIInfoDelayRec(); for (var ele in arrMI) this.smDelayRec.menu.addMenuItem(arrMI[ele]); var secDelay = this._settings.getOption('i', Settings.TIME_DELAY_SETTING_KEY); if (secDelay > 0) { this.smDelayRec.label.text = secDelay + _(' sec. delay before recording'); } else { this.smDelayRec.label.text = _('Start recording immediately'); } this.menu.addMenuItem(this.smDelayRec); } /** * @returns {Array} * @private */ _createMIAreaRec() { this.AreaType = [ _('Record all desktop'), _('Record a selected monitor'), _('Record a selected window'), _('Record a selected area'), ]; this.AreaMenuItem = new Array(this.AreaType.length); for (var i = 0; i < this.AreaMenuItem.length; i++) { this.AreaMenuItem[i] = new PopupMenu.PopupMenuItem( this.AreaType[i], { reactive: true, activate: true, hover: true, can_focus: true, } ); (function (areaSetting, arr, item, settings) { this.connectMI = function () { this.connect('activate', () => { Lib.TalkativeLog(`-*-set area recording to ${areaSetting} ${arr[areaSetting]}`); settings.setOption(Settings.AREA_SCREEN_SETTING_KEY, areaSetting); item.label.text = arr[areaSetting]; }); }; this.connectMI(); }.call( this.AreaMenuItem[i], i, this.AreaType, this.sub_menu_area_recording, this._settings )); } return this.AreaMenuItem; } /** * @returns {Array} * @private */ _createMIWebCam() { this.WebCamDevice = [_('No WebCam recording')]; // add menu item webcam device from GST const devices = this.CtrlWebcam.getDevicesIV(); this.WebCamDevice.push(...this.CtrlWebcam.getNameDevices()); Lib.TalkativeLog(`-*-webcam list: ${this.WebCamDevice}`); this.AreaMenuItem = new Array(this.WebCamDevice.length); for (var i = 0; i < this.AreaMenuItem.length; i++) { let devicePath = ''; // i === 0 is "No Webcam selected" if (i > 0) { const device = devices[i - 1]; devicePath = device.get_properties().get_string('device.path'); Lib.TalkativeLog(`-*-webcam i=${i} devicePath: ${devicePath}`); } Lib.TalkativeLog(`-*-webcam i=${i} menu-item-text: ${this.WebCamDevice[i]}`); this.AreaMenuItem[i] = new PopupMenu.PopupMenuItem( this.WebCamDevice[i], { reactive: true, activate: true, hover: true, can_focus: true, } ); (function (index, devPath, arr, item, settings) { this.connectMI = function () { this.connect('activate', () => { Lib.TalkativeLog(`-*-set webcam device to ${index} ${arr[index]} devicePath=${devPath}`); settings.setOption(Settings.DEVICE_INDEX_WEBCAM_SETTING_KEY, index); settings.setOption(Settings.DEVICE_WEBCAM_SETTING_KEY, devPath); item.label.text = arr[index]; }); }; this.connectMI(); }.call( this.AreaMenuItem[i], i, devicePath, this.WebCamDevice, this.smWebCam, this._settings )); } return this.AreaMenuItem; } /** * @returns {Array} * @private */ _createMIAudioRec() { // add std menu item this.AudioChoice = [ { desc: _('No audio source'), name: 'N/A', port: 'N/A', sortable: true, resizeable: true, }, { desc: _('Default audio source'), name: 'N/A', port: 'N/A', sortable: true, resizeable: true, }, ]; // add menu item audio source from PA var audioList = this.CtrlAudio.getListInputAudio(); for (var index in audioList) this.AudioChoice.push(audioList[index]); this.AudioMenuItem = new Array(this.AudioChoice.length); for (var i = 0; i < this.AudioChoice.length; i++) { // create label menu let labelMenu = this.AudioChoice[i].desc; if (i >= 2) { labelMenu += _('\n - Port: ') + this.AudioChoice[i].port + _('\n - Name: ') + this.AudioChoice[i].name; } // create submenu this.AudioMenuItem[i] = new PopupMenu.PopupMenuItem(labelMenu, { reactive: true, activate: true, hover: true, can_focus: true, }); // add icon on submenu this.AudioMenuItem[i].actor.insert_child_at_index( new St.Icon({ style_class: 'popup-menu-icon', icon_name: 'audio-card-symbolic', }), 1 ); // update choice audio from pref if (i === this._settings.getOption('i', Settings.INPUT_AUDIO_SOURCE_SETTING_KEY)) { Lib.TalkativeLog(`-*-get audio choice from pref ${i}`); this.sub_menu_audio_recording.label.text = this.AudioChoice[i].desc; } // add action on menu item (function (audioIndex, arr, item, settings) { this.connectMI = function () { this.connect('activate', () => { Lib.TalkativeLog(`-*-set audio choice to ${audioIndex}`); settings.setOption(Settings.INPUT_AUDIO_SOURCE_SETTING_KEY, audioIndex); item.label.text = arr[audioIndex].desc; }); }; this.connectMI(); }.call( this.AudioMenuItem[i], i, this.AudioChoice, this.sub_menu_audio_recording, this._settings )); } return this.AudioMenuItem; } /** * @returns {Array} * @private */ _createMIInfoDelayRec() { this.DelayTimeTitle = new PopupMenu.PopupMenuItem(_('Delay Time'), { reactive: false, }); this.DelayTimeLabel = new St.Label({ text: Math.floor( this._settings.getOption('i', Settings.TIME_DELAY_SETTING_KEY) ).toString() + _(' Sec'), }); this.DelayTimeTitle.actor.add_child(this.DelayTimeLabel); // TODO this.DelayTimeTitle.align = St.Align.END; this.imSliderDelay = new PopupMenu.PopupBaseMenuItem({ activate: false, }); this.TimeSlider = new Slider.Slider(this._settings.getOption('i', Settings.TIME_DELAY_SETTING_KEY) / 100); this.TimeSlider.x_expand = true; this.TimeSlider.y_expand = true; this.TimeSlider.connect('notify::value', item => { this.DelayTimeLabel.set_text( Math.floor(item.value * 100).toString() + _(' Sec') ); }); this.TimeSlider.connect('drag-end', () => this._onDelayTimeChanged()); this.TimeSlider.connect('scroll-event', () => this._onDelayTimeChanged() ); this.imSliderDelay.actor.add_child(this.TimeSlider); return [this.DelayTimeTitle, this.imSliderDelay]; } /** * @private */ _enable() { // enable key binding this._enableKeybindings(); // immediately activate/deactive shortcut on settings change this._settings._settings.connect( `changed::${Settings.ACTIVE_SHORTCUT_SETTING_KEY}`, () => { if (this._settings.getOption('b', Settings.ACTIVE_SHORTCUT_SETTING_KEY)) { Lib.TalkativeLog('-^-shortcut changed - enabling'); this._enableKeybindings(); } else { Lib.TalkativeLog('-^-shortcut changed - disabling'); this._removeKeybindings(); } } ); // start monitoring inputvideo this.CtrlWebcam.startMonitor(); // add indicator this.add_child(this.indicatorBox); } /** * @private */ _disable() { // remove key binding this._removeKeybindings(); // stop monitoring inputvideo this.CtrlWebcam.stopMonitor(); // unregister mixer control this.CtrlAudio.destroy(); // remove indicator this.remove_child(this.indicatorBox); } /** * @private */ _doDelayAction() { if (this.isDelayActive) { Lib.TalkativeLog(`-*-delay recording called | delay= ${this.TimeSlider.value}`); timerD = new Time.TimerDelay( Math.floor(this.TimeSlider.value * 100), this._doPreCommand, this ); timerD.begin(); } else { Lib.TalkativeLog('-*-instant recording called'); // start recording this._doPreCommand(); } } /** * @private */ _doPreCommand() { if (this._settings.getOption('b', Settings.ACTIVE_PRE_CMD_SETTING_KEY)) { Lib.TalkativeLog('-*-execute pre command'); const PreCmd = this._settings.getOption('s', Settings.PRE_CMD_SETTING_KEY); this.CtrlExe.Execute( PreCmd, false, res => { Lib.TalkativeLog(`-*-pre command final: ${res}`); if (res === true) { Lib.TalkativeLog('-*-pre command OK'); this.recorder.start(); } else { Lib.TalkativeLog('-*-pre command ERROR'); this.CtrlNotify.createNotify( _('ERROR PRE COMMAND - See logs for more info'), this._icons.off ); } }, line => { Lib.TalkativeLog(`-*-pre command output: ${line}`); } ); } else { this.recorder.start(); } } /** * @private */ _doRecording() { // start/stop record screen if (isActive === false) { Lib.TalkativeLog('-*-start recording'); pathFile = ''; // get selected area const optArea = this._settings.getOption('i', Settings.AREA_SCREEN_SETTING_KEY); if (optArea > 0) { Lib.TalkativeLog(`-*-type of selection of the area to record: ${optArea}`); switch (optArea) { case 3: new Selection.SelectionArea(); break; case 2: new Selection.SelectionWindow(); break; case 1: new Selection.SelectionDesktop(); break; } } else { Lib.TalkativeLog('-*-recording full area'); this._doDelayAction(); } } else { Lib.TalkativeLog('-*-stop recording'); isActive = false; this.recorder.stop(); if (timerC !== null) { // stop counting rec timerC.halt(); timerC = null; } // execute post-command this._doPostCommand(); } this.refreshIndicator(false); } /** * @private */ _doPostCommand() { if (this._settings.getOption('b', Settings.ACTIVE_POST_CMD_SETTING_KEY)) { Lib.TalkativeLog('-*-execute post command'); // launch cmd after registration const tmpCmd = `/usr/bin/sh -c "${this._settings.getOption('s', Settings.POST_CMD_SETTING_KEY)}"`; const mapObj = { _fpath: pathFile, _dirpath: pathFile.substr(0, pathFile.lastIndexOf('/')), _fname: pathFile.substr( pathFile.lastIndexOf('/') + 1, pathFile.length ), }; const Cmd = tmpCmd.replace(/_fpath|_dirpath|_fname/gi, match => { return mapObj[match]; }); Lib.TalkativeLog(`-*-post command:${Cmd}`); // execute post command this.CtrlExe.Spawn(Cmd); } } /** * @param {boolean} result whether the recording was successful * @param {string} file file path of the recorded file */ doRecResult(result, file) { if (result) { isActive = true; Lib.TalkativeLog('-*-record OK'); // update indicator const indicators = this._settings.getOption('i', Settings.STATUS_INDICATORS_SETTING_KEY); this._replaceStdIndicator(indicators === 1 || indicators === 3); if (this.isShowNotify) { Lib.TalkativeLog('-*-show notify'); // create counting notify this.notifyCounting = this.CtrlNotify.createNotify( _('Start Recording'), this._icons.on ); this.notifyCounting.connect('destroy', () => { Lib.TalkativeLog('-*-notification destroyed'); this.notifyCounting = null; }); // start counting rec timerC = new Time.TimerCounting((secpassed, alertEnd) => { this._refreshNotify(secpassed, alertEnd); }, this); timerC.begin(); } // update path file video pathFile = file; Lib.TalkativeLog(`-*-update abs file path -> ${pathFile}`); } else { Lib.TalkativeLog('-*-record ERROR'); pathFile = ''; if (this.isShowNotify) { Lib.TalkativeLog('-*-show error notify'); this.CtrlNotify.createNotify( _('ERROR RECORDER - See logs for more info'), this._icons.off ); } } this.refreshIndicator(false); } /** * @param {number} sec the seconds passed * @param {boolean} alertEnd whether the timer is ending */ _refreshNotify(sec, alertEnd) { if (this.notifyCounting !== null && this.notifyCounting !== undefined && this.isShowNotify) { if (alertEnd) { this.CtrlNotify.updateNotify( this.notifyCounting, _(`Finish Recording / Seconds : ${sec}`), this._icons.off, true ); } else { this.CtrlNotify.updateNotify( this.notifyCounting, _('Recording ... / Seconds passed : ') + sec, this._icons.on, false ); } } } /** * @private */ _openExtensionPreferences() { try { this._extension.openPreferences(); } catch (e) { Lib.TalkativeLog(`Failed to open preferences: ${e}`); } } /** * @private */ _onDelayTimeChanged() { const secDelay = Math.floor(this.TimeSlider.value * 100); this._settings.setOption(Settings.TIME_DELAY_SETTING_KEY, secDelay); if (secDelay > 0) this.smDelayRec.label.text = secDelay + _(' sec. delay before recording'); else this.smDelayRec.label.text = _('Start recording immediately'); } /** * @param {boolean} focus selects the correct icon depending on the focus state */ refreshIndicator(focus) { Lib.TalkativeLog(`-*-refresh indicator -A ${isActive} -F ${focus}`); const indicators = this._settings.getOption('i', Settings.STATUS_INDICATORS_SETTING_KEY); if (isActive === true) { if (indicators === 0 || indicators === 1) { if (focus === true) this.indicatorIcon.set_gicon(this._icons.onSel); else this.indicatorIcon.set_gicon(this._icons.on); } else if (this._settings.getOption('b', Settings.ACTIVE_SHORTCUT_SETTING_KEY)) { this.indicatorIcon.set_gicon(null); } else if (focus === true) { this.indicatorIcon.set_gicon(this._icons.onSel); } else { this.indicatorIcon.set_gicon(this._icons.on); } this.RecordingLabel.set_text(_('Stop recording')); } else { if (focus === true) this.indicatorIcon.set_gicon(this._icons.offSel); else this.indicatorIcon.set_gicon(this._icons.off); this.RecordingLabel.set_text(_('Start recording')); } } /** * @param {boolean} OPTtemp whether to replace the standard indicator or use it * @private */ _replaceStdIndicator(OPTtemp) { if (Main.panel.statusArea === undefined) { Lib.TalkativeLog('-*-no Main.panel.statusArea found'); return; } var stdMenu = Main.panel.statusArea.quickSettings; if (stdMenu === undefined) { Lib.TalkativeLog('-*-no quickSettings or aggregateMenu in Main.panel.statusArea'); return; } if (stdMenu._remoteAccess === undefined) { Lib.TalkativeLog('-*-no _remoteAccess indicator applet found'); return; } var indicator = stdMenu._remoteAccess._indicator; if (indicator === undefined) { Lib.TalkativeLog('-*-no _indicator or _recordingIndicator found'); return; } if (OPTtemp) { Lib.TalkativeLog('-*-replace STD indicator'); indicator.visible = false; } else { Lib.TalkativeLog('-*-use STD indicator'); indicator.visible = isActive; } } /** * @private */ _enableKeybindings() { if (this._settings.getOption('b', Settings.ACTIVE_SHORTCUT_SETTING_KEY)) { Lib.TalkativeLog('-*-enable keybinding'); Main.wm.addKeybinding( Settings.SHORTCUT_KEY_SETTING_KEY, this._settings._settings, Meta.KeyBindingFlags.NONE, // available modes: https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/src/shell-action-modes.h Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW | Shell.ActionMode.POPUP | Shell.ActionMode.SYSTEM_MODAL, () => { Lib.TalkativeLog('-*-intercept key combination'); this._doRecording(); } ); keybindingConfigured = true; } } /** * @private */ _removeKeybindings() { if (keybindingConfigured) { Lib.TalkativeLog('-*-remove keybinding'); Main.wm.removeKeybinding(Settings.SHORTCUT_KEY_SETTING_KEY); keybindingConfigured = false; } } getSelectedRect() { var recX = this._settings.getOption('i', Settings.X_POS_SETTING_KEY); var recY = this._settings.getOption('i', Settings.Y_POS_SETTING_KEY); var recW = this._settings.getOption('i', Settings.WIDTH_SETTING_KEY); var recH = this._settings.getOption('i', Settings.HEIGHT_SETTING_KEY); return [recX, recY, recW, recH]; } saveSelectedRect(x, y, h, w) { this._settings.setOption(Settings.X_POS_SETTING_KEY, x); this._settings.setOption(Settings.Y_POS_SETTING_KEY, y); this._settings.setOption(Settings.HEIGHT_SETTING_KEY, h); this._settings.setOption(Settings.WIDTH_SETTING_KEY, w); } getSettings() { return this._settings; } getAudioSource() { return this.CtrlAudio.getAudioSource(); } /** * Destroy indicator */ destroy() { Lib.TalkativeLog('-*-destroy indicator called'); if (isActive) isActive = false; if (this._settings) { this._settings.destroy(); this._settings = null; } super.destroy(); } }); export default class EasyScreenCast extends Extension { /** * */ enable() { Lib.TalkativeLog('-*-enableExtension called'); Lib.TalkativeLog(`-*-version: ${this.metadata.version}`); Lib.TalkativeLog(`-*-install path: ${this.path}`); Lib.TalkativeLog(`-*-version (package.json): ${Lib.getFullVersion()}`); if (Indicator === null || Indicator === undefined) { Lib.TalkativeLog('-*-create indicator'); Indicator = new EasyScreenCastIndicator(this); Main.panel.addToStatusArea('EasyScreenCast-indicator', Indicator); } Indicator._enable(); } /** * */ disable() { Lib.TalkativeLog('-*-disableExtension called'); if (timerD !== null) { Lib.TalkativeLog('-*-timerD stopped'); timerD.stop(); timerD = null; } // this might happen, if the extension is disabled during recording if (timerC !== null) { // stop counting rec timerC.halt(); timerC = null; } if (Indicator !== null) { Lib.TalkativeLog('-*-indicator call destroy'); Indicator._disable(); Indicator.destroy(); Indicator = null; } } } export {Indicator};