Blob Blame History Raw
const { Clutter, Gio, GLib, GObject, Gtk, Meta, Shell } = imports.gi;

const Dialog = imports.ui.dialog;
const ModalDialog = imports.ui.modalDialog;
const PermissionStore = imports.misc.permissionStore;

const WAYLAND_KEYBINDINGS_SCHEMA = 'org.gnome.mutter.wayland.keybindings';

const APP_WHITELIST = ['gnome-control-center.desktop'];
const APP_PERMISSIONS_TABLE = 'gnome';
const APP_PERMISSIONS_ID = 'shortcuts-inhibitor';
const GRANTED = 'GRANTED';
const DENIED = 'DENIED';

var DialogResponse = Meta.InhibitShortcutsDialogResponse;

var InhibitShortcutsDialog = GObject.registerClass({
    Implements: [Meta.InhibitShortcutsDialog],
    Properties: {
        'window': GObject.ParamSpec.override('window', Meta.InhibitShortcutsDialog)
    }
}, class InhibitShortcutsDialog extends GObject.Object {
    _init(window) {
        super._init();
        this._window = window;

        this._dialog = new ModalDialog.ModalDialog();
        this._buildLayout();
    }

    get window() {
        return this._window;
    }

    set window(window) {
        this._window = window;
    }

    get _app() {
        let windowTracker = Shell.WindowTracker.get_default();
        return windowTracker.get_window_app(this._window);
    }

    _getRestoreAccel() {
        let settings = new Gio.Settings({ schema_id: WAYLAND_KEYBINDINGS_SCHEMA });
        let accel = settings.get_strv('restore-shortcuts')[0] || '';
        return Gtk.accelerator_get_label.apply(null,
                                               Gtk.accelerator_parse(accel));
    }

    _shouldUsePermStore() {
        return this._app && !this._app.is_window_backed();
    }

    _saveToPermissionStore(grant) {
        if (!this._shouldUsePermStore() || this._permStore == null)
            return;

        let permissions = {};
        permissions[this._app.get_id()] = [grant];
        let data = GLib.Variant.new('av', {});

        this._permStore.SetRemote(APP_PERMISSIONS_TABLE,
                                  true,
                                  APP_PERMISSIONS_ID,
                                  permissions,
                                  data,
            (result, error) => {
                if (error != null)
                    log(error.message);
            });
    }

    _buildLayout() {
        let name = this._app ? this._app.get_name() : this._window.title;

        /* Translators: %s is an application name like "Settings" */
        let title = name ? _("%s wants to inhibit shortcuts").format(name)
                         : _("Application wants to inhibit shortcuts");
        let icon = new Gio.ThemedIcon({ name: 'dialog-warning-symbolic' });

        let contentParams = { icon, title };

        let restoreAccel = this._getRestoreAccel();
        if (restoreAccel)
            contentParams.subtitle =
                /* Translators: %s is a keyboard shortcut like "Super+x" */
                _("You can restore shortcuts by pressing %s.").format(restoreAccel);

        let content = new Dialog.MessageDialogContent(contentParams);
        this._dialog.contentLayout.add_actor(content);

        this._dialog.addButton({ label: _("Deny"),
                                 action: () => {
                                     this._saveToPermissionStore(DENIED);
                                     this._emitResponse(DialogResponse.DENY);
                                 },
                                 key: Clutter.KEY_Escape });

        this._dialog.addButton({ label: _("Allow"),
                                 action: () => {
                                     this._saveToPermissionStore(GRANTED);
                                     this._emitResponse(DialogResponse.ALLOW);
                                 },
                                 default: true });
    }

    _emitResponse(response) {
        this.emit('response', response);
        this._dialog.close();
    }

    vfunc_show() {
        if (this._app && APP_WHITELIST.indexOf(this._app.get_id()) != -1) {
            this._emitResponse(DialogResponse.ALLOW);
            return;
        }

        if (!this._shouldUsePermStore()) {
            this._dialog.open();
            return;
        }

        /* Check with the permission store */
        let appId = this._app.get_id();
        this._permStore = new PermissionStore.PermissionStore((proxy, error) => {
            if (error) {
                log(error.message);
                this._dialog.open();
                return;
            }

            this._permStore.LookupRemote(APP_PERMISSIONS_TABLE,
                                         APP_PERMISSIONS_ID,
                (res, error) => {
                    if (error) {
                        this._dialog.open();
                        log(error.message);
                        return;
                    }

                    let [permissions, data] = res;
                    if (permissions[appId] === undefined) // Not found
                        this._dialog.open();
                    else if (permissions[appId] == GRANTED)
                        this._emitResponse(DialogResponse.ALLOW);
                    else
                        this._emitResponse(DialogResponse.DENY);
                });
        });
    }

    vfunc_hide() {
        this._dialog.close();
    }
});