[gnome-shell] lookingGlass: Add switches for debug flags



commit ac4412e4aa7be7c65ecf33b03bfd4e89d7b2fea4
Author: Sebastian Keller <skeller gnome org>
Date:   Wed Sep 29 20:09:19 2021 +0200

    lookingGlass: Add switches for debug flags
    
    This adds a new "Flags" tab to looking glass where several debug flags
    can be toggled at run time. This includes:
    
    ClutterDebugFlags
    ClutterDrawDebugFlags
    MetaDebugPaintFlags
    MetaDebugTopics
    MetaContext::unsafe-mode
    
    Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/3968
    Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1994>

 .../gnome-shell-sass/widgets/_looking-glass.scss   |  16 +-
 js/ui/lookingGlass.js                              | 225 +++++++++++++++++++++
 2 files changed, 240 insertions(+), 1 deletion(-)
---
diff --git a/data/theme/gnome-shell-sass/widgets/_looking-glass.scss 
b/data/theme/gnome-shell-sass/widgets/_looking-glass.scss
index 006c2ef920..ec27aac78d 100644
--- a/data/theme/gnome-shell-sass/widgets/_looking-glass.scss
+++ b/data/theme/gnome-shell-sass/widgets/_looking-glass.scss
@@ -106,4 +106,18 @@ $text_fg_color: #ccc;
   border: 1px solid $osd_borders_color;
   border-radius: $base_border_radius;
   padding: 6px;
-}
\ No newline at end of file
+}
+
+.lg-debug-flag-button {
+  StLabel { padding: $base_spacing, 2 * $base_spacing; }
+
+  color: $text_fg_color;
+  &:hover { color: lighten($text_fg_color, 20%); }
+  &:active { color: darken($text_fg_color, 20%); }
+}
+
+.lg-debug-flags-header {
+  padding-top: 2 * $base_spacing;
+  font-size: 120%;
+  font-weight: bold;
+}
diff --git a/js/ui/lookingGlass.js b/js/ui/lookingGlass.js
index cc5a7e5e47..433102cfb6 100644
--- a/js/ui/lookingGlass.js
+++ b/js/ui/lookingGlass.js
@@ -8,6 +8,7 @@ const System = imports.system;
 
 const History = imports.misc.history;
 const ExtensionUtils = imports.misc.extensionUtils;
+const PopupMenu = imports.ui.popupMenu;
 const ShellEntry = imports.ui.shellEntry;
 const Main = imports.ui.main;
 const JsParse = imports.misc.jsParse;
@@ -35,6 +36,15 @@ var AUTO_COMPLETE_GLOBAL_KEYWORDS = _getAutoCompleteGlobalKeywords();
 
 const LG_ANIMATION_TIME = 500;
 
+const CLUTTER_DEBUG_FLAG_CATEGORIES = new Map([
+    // Paint debugging can easily result in a non-responsive session
+    ['DebugFlag', { argPos: 0, exclude: ['PAINT'] }],
+    ['DrawDebugFlag', { argPos: 1, exclude: [] }],
+    // Exluded due to the only current option likely to result in shooting ones
+    // foot
+    // ['PickDebugFlag', { argPos: 2, exclude: [] }],
+]);
+
 function _getAutoCompleteGlobalKeywords() {
     const keywords = ['true', 'false', 'null', 'new'];
     // Don't add the private properties of globalThis (i.e., ones starting with '_')
@@ -1003,6 +1013,218 @@ class ActorTreeViewer extends St.BoxLayout {
     }
 });
 
+var DebugFlag = GObject.registerClass({
+    GTypeFlags: GObject.TypeFlags.ABSTRACT,
+}, class DebugFlag extends St.Button {
+    _init(label) {
+        const box = new St.BoxLayout();
+
+        const flagLabel = new St.Label({
+            text: label,
+            x_expand: true,
+            x_align: Clutter.ActorAlign.START,
+            y_align: Clutter.ActorAlign.CENTER,
+        });
+        box.add_child(flagLabel);
+
+        this._flagSwitch = new PopupMenu.Switch(false);
+        this._stateHandler = this._flagSwitch.connect('notify::state', () => {
+            if (this._flagSwitch.state)
+                this._enable();
+            else
+                this._disable();
+        });
+
+        // Update state whenever the switch is mapped, because most debug flags
+        // don't have a way of notifying us of changes.
+        this._flagSwitch.connect('notify::mapped', () => {
+            if (!this._flagSwitch.is_mapped())
+                return;
+
+            const state = this._isEnabled();
+            if (state === this._flagSwitch.state)
+                return;
+
+            this._flagSwitch.block_signal_handler(this._stateHandler);
+            this._flagSwitch.state = state;
+            this._flagSwitch.unblock_signal_handler(this._stateHandler);
+        });
+
+        box.add_child(this._flagSwitch);
+
+        super._init({
+            style_class: 'lg-debug-flag-button',
+            can_focus: true,
+            toggleMode: true,
+            child: box,
+            label_actor: flagLabel,
+            y_align: Clutter.ActorAlign.CENTER,
+        });
+
+        this.connect('clicked', () => this._flagSwitch.toggle());
+    }
+
+    _isEnabled() {
+        throw new Error('Method not implemented');
+    }
+
+    _enable() {
+        throw new Error('Method not implemented');
+    }
+
+    _disable() {
+        throw new Error('Method not implemented');
+    }
+});
+
+
+var ClutterDebugFlag = GObject.registerClass(
+class ClutterDebugFlag extends DebugFlag {
+    _init(categoryName, flagName) {
+        super._init(flagName);
+
+        this._argPos = CLUTTER_DEBUG_FLAG_CATEGORIES.get(categoryName).argPos;
+        this._enumValue = Clutter[categoryName][flagName];
+    }
+
+    _isEnabled() {
+        const enabledFlags = Meta.get_clutter_debug_flags();
+        return !!(enabledFlags[this._argPos] & this._enumValue);
+    }
+
+    _getArgs() {
+        const args = [0, 0, 0];
+        args[this._argPos] = this._enumValue;
+        return args;
+    }
+
+    _enable() {
+        Meta.add_clutter_debug_flags(...this._getArgs());
+    }
+
+    _disable() {
+        Meta.remove_clutter_debug_flags(...this._getArgs());
+    }
+});
+
+var MutterPaintDebugFlag = GObject.registerClass(
+class MutterPaintDebugFlag extends DebugFlag {
+    _init(flagName) {
+        super._init(flagName);
+
+        this._enumValue = Meta.DebugPaintFlag[flagName];
+    }
+
+    _isEnabled() {
+        return !!(Meta.get_debug_paint_flags() & this._enumValue);
+    }
+
+    _enable() {
+        Meta.add_debug_paint_flag(this._enumValue);
+    }
+
+    _disable() {
+        Meta.remove_debug_paint_flag(this._enumValue);
+    }
+});
+
+var MutterTopicDebugFlag = GObject.registerClass(
+class MutterTopicDebugFlag extends DebugFlag {
+    _init(flagName) {
+        super._init(flagName);
+
+        this._enumValue = Meta.DebugTopic[flagName];
+    }
+
+    _isEnabled() {
+        return Meta.is_topic_enabled(this._enumValue);
+    }
+
+    _enable() {
+        Meta.add_verbose_topic(this._enumValue);
+    }
+
+    _disable() {
+        Meta.remove_verbose_topic(this._enumValue);
+    }
+});
+
+var UnsafeModeDebugFlag = GObject.registerClass(
+class UnsafeModeDebugFlag extends DebugFlag {
+    _init() {
+        super._init('unsafe-mode');
+    }
+
+    _isEnabled() {
+        return global.context.unsafe_mode;
+    }
+
+    _enable() {
+        global.context.unsafe_mode = true;
+    }
+
+    _disable() {
+        global.context.unsafe_mode = false;
+    }
+});
+
+var DebugFlags = GObject.registerClass(
+class DebugFlags extends St.BoxLayout {
+    _init() {
+        super._init({
+            name: 'lookingGlassDebugFlags',
+            vertical: true,
+            x_align: Clutter.ActorAlign.CENTER,
+        });
+
+        // Clutter debug flags
+        for (const [categoryName, props] of CLUTTER_DEBUG_FLAG_CATEGORIES.entries()) {
+            this._addHeader('Clutter%s'.format(categoryName));
+            for (const flagName of this._getFlagNames(Clutter[categoryName])) {
+                if (props.exclude.includes(flagName))
+                    continue;
+                this.add_child(new ClutterDebugFlag(categoryName, flagName));
+            }
+        }
+
+        // Meta paint flags
+        this._addHeader('MetaDebugPaintFlag');
+        for (const flagName of this._getFlagNames(Meta.DebugPaintFlag))
+            this.add_child(new MutterPaintDebugFlag(flagName));
+
+        // Meta debug topics
+        this._addHeader('MetaDebugTopic');
+        for (const flagName of this._getFlagNames(Meta.DebugTopic))
+            this.add_child(new MutterTopicDebugFlag(flagName));
+
+        // MetaContext::unsafe-mode
+        this._addHeader('MetaContext');
+        this.add_child(new UnsafeModeDebugFlag());
+    }
+
+    _addHeader(title) {
+        const header = new St.Label({
+            text: title,
+            style_class: 'lg-debug-flags-header',
+            x_align: Clutter.ActorAlign.START,
+        });
+        this.add_child(header);
+    }
+
+    *_getFlagNames(enumObject) {
+        for (const flagName of Object.getOwnPropertyNames(enumObject)) {
+            if (typeof enumObject[flagName] !== 'number')
+                continue;
+
+            if (enumObject[flagName] <= 0)
+                continue;
+
+            yield flagName;
+        }
+    }
+});
+
+
 var LookingGlass = GObject.registerClass(
 class LookingGlass extends St.BoxLayout {
     _init() {
@@ -1124,6 +1346,9 @@ class LookingGlass extends St.BoxLayout {
         this._actorTreeViewer = new ActorTreeViewer(this);
         notebook.appendPage('Actors', this._actorTreeViewer);
 
+        this._debugFlags = new DebugFlags();
+        notebook.appendPage('Flags', this._debugFlags);
+
         this._entry.clutter_text.connect('activate', (o, _e) => {
             // Hide any completions we are currently showing
             this._hideCompletions();


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]