diff --git a/js/ui/lookingGlass.js b/js/ui/lookingGlass.js index afe99b864..5aad6e427 100644 --- a/js/ui/lookingGlass.js +++ b/js/ui/lookingGlass.js @@ -308,6 +308,42 @@ Inspector.prototype = { Signals.addSignalMethods(Inspector.prototype); +function ErrorLog() { + this._init(); +} + +ErrorLog.prototype = { + _init: function() { + this.actor = new St.BoxLayout(); + this.text = new St.Label(); + this.actor.add(this.text); + this.text.clutter_text.line_wrap = true; + this.text.connect('notify::mapped', Lang.bind(this, this._onMappedNotify)); + }, + + _formatTime: function(d){ + function pad(n) { return n < 10 ? '0' + n : n }; + return d.getUTCFullYear()+'-' + + pad(d.getUTCMonth()+1)+'-' + + pad(d.getUTCDate())+'T' + + pad(d.getUTCHours())+':' + + pad(d.getUTCMinutes())+':' + + pad(d.getUTCSeconds())+'Z' + }, + + _onMappedNotify: function() { + if (!(this.actor.mapped && Main._errorLogStack.length > 0)) + return; + let text = this.text.text; + let stack = Main._getAndClearErrorStack(); + for (let i = 0; i < stack.length; i++) { + let logItem = stack[i]; + text += logItem.category + " t=" + this._formatTime(new Date(logItem.timestamp)) + " " + logItem.message + "\n"; + } + this.text.text = text; + } +} + function LookingGlass() { this._init(); } @@ -406,6 +442,9 @@ LookingGlass.prototype = { notebook.selectIndex(0); })); + this._errorLog = new ErrorLog(); + notebook.appendPage('Errors', this._errorLog.actor); + this._entry.clutter_text.connect('activate', Lang.bind(this, function (o, e) { let text = o.get_text(); // Ensure we don't get newlines in the command; the history file is diff --git a/js/ui/main.js b/js/ui/main.js index 5a0ad4cc9..ed24fc373 100644 --- a/js/ui/main.js +++ b/js/ui/main.js @@ -38,6 +38,8 @@ let recorder = null; let shellDBusService = null; let modalCount = 0; let modalActorFocusStack = []; +let _errorLogStack = []; +let _startDate; function start() { // Add a binding for "global" in the global JS namespace; (gjs @@ -45,6 +47,12 @@ function start() { // called "window".) window.global = Shell.Global.get(); + // Now monkey patch utility functions into the global proxy; + // This is easier and faster than indirecting down into global + // if we want to call back up into JS. + global.logError = _logError; + global.log = _logDebug; + Gio.DesktopAppInfo.set_desktop_env("GNOME"); global.grab_dbus_service(); @@ -105,6 +113,8 @@ function start() { sidebar = new Sidebar.Sidebar(); wm = new WindowManager.WindowManager(); + _startDate = new Date(); + global.screen.connect('toggle-recording', function() { if (recorder == null) { recorder = new Shell.Recorder({ stage: global.stage }); @@ -127,9 +137,52 @@ function start() { global.stage.connect('captured-event', _globalKeyPressHandler); + _log('info', 'loaded at ' + _startDate); + Mainloop.idle_add(_removeUnusedWorkspaces); } +/** + * _log: + * @category: string message type ('info', 'error') + * @msg: A message string + * ...: Any further arguments are converted into JSON notation, + * and appended to the log message, separated by spaces. + * + * Log a message into the LookingGlass error + * stream. This is primarily intended for use by the + * extension system as well as debugging. + */ +function _log(category, msg) { + let text = msg; + if (arguments.length > 2) { + text += ': '; + for (let i = 2; i < arguments.length; i++) { + text += JSON.stringify(arguments[i]); + if (i < arguments.length - 1) + text += " "; + } + } + _errorLogStack.push({timestamp: new Date().getTime(), + category: category, + message: text }); +} + +function _logError(msg) { + return _log('error', msg); +} + +function _logDebug(msg) { + return _log('debug', msg); +} + +// Used by the error display in lookingGlass.js +function _getAndClearErrorStack() { + let errors = _errorLogStack; + _errorLogStack = []; + return errors; +} + function _relayout() { let primary = global.get_primary_monitor(); panel.actor.set_position(primary.x, primary.y);