/******************************************************************************* * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . * ***************************************************************************** * Original Author: Gopi Sankar Karmegam ******************************************************************************/ /* jshint moz:true */ const ByteArray = imports.byteArray; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const ExtensionUtils = imports.misc.extensionUtils; const Me = ExtensionUtils.getCurrentExtension(); const Prefs = Me.imports.prefs; var DEBUG = false; var logWrap; if(log != undefined){ logWrap = log; } else { logWrap = global.log } /** * getSettings: * * @schema: (optional): the GSettings schema id Builds and return a GSettings * schema for * @schema, using schema files in extensions dir/schemas. If * @schema is not provided, it is taken from metadata['settings-schema']. */ function getSettings(schema) { // let extension = ExtensionUtils.getCurrentExtension(); schema = schema || Me.metadata['settings-schema']; const GioSSS = Gio.SettingsSchemaSource; // check if this extension was built with "make zip-file", and thus // has the schema files in a sub-folder // otherwise assume that extension has been installed in the // same prefix as gnome-shell (and therefore schemas are available // in the standard folders) let schemaDir = Me.dir.get_child('schemas'); let schemaSource; if (schemaDir.query_exists(null)) schemaSource = GioSSS.new_from_directory(schemaDir.get_path(),GioSSS.get_default(),false); else schemaSource = GioSSS.get_default(); let schemaObj = schemaSource.lookup(schema, true); if (!schemaObj) throw new Error('Schema ' + schema + ' could not be found for extension ' + Me.metadata.uuid + '. Please check your installation.'); let _settings = new Gio.Settings({ settings_schema: schemaObj }); return _settings; } let cards; function getProfiles(control, uidevice) { let stream = control.lookup_stream_id(uidevice.get_stream_id()); if(stream) { if(!cards || Object.keys(cards).length == 0 || !cards[stream.card_index]) { refreshCards(); } if(cards && cards[stream.card_index]) { _log("Getting profile form stream id " + uidevice.port_name ); return getProfilesForPort(uidevice.port_name, cards[stream.card_index]); } } else { /* Device is not active device, lets try match with port name */ refreshCards(); for (let id in cards) { let profiles; _log("Getting profile from cards " + uidevice.port_name + " for card id " + id); if((profiles = getProfilesForPort(uidevice.port_name, cards[id]))) { return profiles; } } } return null; } let ports; function getPorts(refresh) { if(!ports || ports.length == 0 || refresh) { refreshCards(); } return ports; } function isCmdFound(cmd){ try { let [result, out, err, exit_code] = GLib.spawn_command_line_sync(cmd); return true; } catch(e) { _log('ERROR: '+ cmd +' execution failed. ' + e); return false; } } function refreshCards() { cards = {}; ports = []; // if(_settings == null) {getSettings(Prefs.SETTINGS_SCHEMA);} let _settings = getSettings(Prefs.SETTINGS_SCHEMA); let error = false; if(_settings.get_boolean(Prefs.NEW_PROFILE_ID)) { _log("New logic"); let pyLocation = Me.dir.get_child('utils/pa_helper.py').get_path(); let pythonExec = 'python'; let pyVer = 3; while(!isCmdFound(pythonExec) && pyVer >=2){ _log(pythonExec + " is not found. Try next"); pythonExec = 'python' + pyVer--; } if(pyVer <= 1) { _log('ERROR: Python not found. fallback to default mode' + e); _settings.set_boolean(Prefs.NEW_PROFILE_ID, false); Gio.Settings.sync(); } else { try { let [result, out, err, exit_code] = GLib.spawn_command_line_sync(pythonExec+ ' ' + pyLocation); // _log("result" + result +" out"+out + " exit_code" + // exit_code + "err" +err); if(result && !exit_code) { if (out instanceof Uint8Array) { out = ByteArray.toString(out); } let obj = JSON.parse(out); cards = obj['cards']; ports = obj['ports']; } } catch(e) { error = true; _log('ERROR: Python execution failed. fallback to default mode' + e); _settings.set_boolean(Prefs.NEW_PROFILE_ID, false); Gio.Settings.sync(); } } } if(!_settings.get_boolean(Prefs.NEW_PROFILE_ID) || error){ try { let [result, out, err, exit_code] = GLib.spawn_command_line_sync('pactl list cards'); if(result && !exit_code) { parseOutput(out); } } catch(e) { _log('ERROR: pactl execution failed. No ports/profiles will be displayed'); } } // _log(JSON.stringify(cards)); // _log(JSON.stringify(ports)); } function parseOutput(out) { let lines; if (out instanceof Uint8Array) { lines = ByteArray.toString(out).split('\n'); } else { lines = out.toString().split('\n'); } let cardIndex; let parseSection = "CARDS"; let port; let matches; // _log("Unmatched line:" + out); while(lines.length > 0) { let line = lines.shift(); if( (matches = /^Card\s#(\d+)$/.exec(line) )) { cardIndex = matches[1]; if(!cards[cardIndex]) { cards[cardIndex] = {'index':cardIndex,'profiles':[], 'ports':[]}; } } else if (line.match(/^\t*Profiles:$/) ) { parseSection = "PROFILES"; } else if (line.match(/^\t*Ports:$/)) { parseSection = "PORTS"; } else if(cards[cardIndex]) { switch(parseSection) { case "PROFILES": if((matches = /.*?((?:output|input)[^+]*?):\s(.*?)\s\(sinks:/.exec(line))) { cards[cardIndex].profiles.push({'name': matches[1], 'human_name': matches[2]}); } break; case "PORTS": if((matches = /\t*(.*?):\s(.*?)\s\(priority:/.exec(line))) { port = {'name' : matches[1], 'human_name' : matches[2]}; cards[cardIndex].ports.push(port); ports.push({'name' : matches[1], 'human_name' : matches[2]}); } else if( port && (matches = /\t*Part of profile\(s\):\s(.*)/.exec(line))) { let profileStr = matches[1]; port.profiles = profileStr.split(', '); port = null; } break; } } } } var Signal = class Signal { constructor(signalSource, signalName, callback) { this._signalSource = signalSource; this._signalName = signalName; this._signalCallback = callback; } connect() { this._signalId = this._signalSource.connect(this._signalName, this._signalCallback); } disconnect() { if(this._signalId) { this._signalSource.disconnect(this._signalId); this._signalId = null; } } } var SignalManager = class SignalManager { constructor() { this._signals = []; this._signalsBySource = {}; } addSignal(signalSource, signalName, callback) { let obj = null; if(signalSource && signalName && callback) { obj = new Signal(signalSource, signalName, callback); obj.connect(); this._signals.push(obj); let sourceSignals = this._signalsBySource[signalSource] if(!sourceSignals) { sourceSignals = []; this._signalsBySource[signalSource] = sourceSignals; } // this._signalsBySource[signalSource].push(obj) sourceSignals.push(obj); } return obj; } disconnectAll() { for (let signal of this._signals){ signal.disconnect(); } } disconnectBySource(signalSource) { if(this._signalsBySource[signalSource]) { for (let signal of this._signalsBySource[signalSource]) { signal.disconnect(); } } } } function getProfilesForPort(portName, card) { if(card.ports) { for (let port of card.ports) { if(portName === port.name) { let profiles = []; if (port.profiles) { for (let profile of port.profiles) { if(profile.indexOf('+input:') == -1) { for (let cardProfile of card.profiles) { if(profile === cardProfile.name) { profiles.push(cardProfile); } } } } } return profiles; } } } return null; } function setLog(value) { DEBUG = value; } function _log(msg) { if ( DEBUG == true ) { // global.log("SDC Debug: " + msg); logWrap("SDC Debug: " + msg); } } function dump(obj) { var propValue; for(var propName in obj) { try{ propValue = obj[propName]; _log(propName + propValue); } catch(e){_log(propName + "!!!Error!!!");} } }