iso-profiles-settings/tromjaro/gnome/desktop-overlay/etc/skel/.local/share/gnome-shell/extensions/unite@hardpixel.eu/window.js

445 lines
9.8 KiB
JavaScript

const Bytes = imports.byteArray
const GLib = imports.gi.GLib
const GObject = imports.gi.GObject
const Meta = imports.gi.Meta
const Main = imports.ui.main
const Util = imports.misc.util
const Unite = imports.misc.extensionUtils.getCurrentExtension()
const AppMenu = Main.panel.statusArea.appMenu
const Handlers = Unite.imports.handlers
const VALID_TYPES = [
Meta.WindowType.NORMAL,
Meta.WindowType.DIALOG,
Meta.WindowType.MODAL_DIALOG,
Meta.WindowType.UTILITY
]
const UNITE_HINTS = '_UNITE_ORIGINAL_STATE'
const MOTIF_HINTS = '_MOTIF_WM_HINTS'
const _SHOW_FLAGS = ['0x2', '0x0', '0x1', '0x0', '0x0']
const _HIDE_FLAGS = ['0x2', '0x0', '0x2', '0x0', '0x0']
function isValid(win) {
return win && VALID_TYPES.includes(win.window_type)
}
function getXid(win) {
const desc = win.get_description()
const match = desc && desc.match(/0x[0-9a-f]+/)
return match && match[0]
}
function getHint(xid, name, fallback) {
const result = GLib.spawn_command_line_sync(`xprop -id ${xid} ${name}`)
const string = Bytes.toString(result[1])
if (!string.match(/=/)) {
return fallback
}
return string.split('=')[1].trim().split(',').map(part => {
part = part.trim()
return part.match(/\dx/) ? part : `0x${part}`
})
}
function setHint(xid, hint, value) {
value = value.join(', ')
Util.spawn(['xprop', '-id', xid, '-f', hint, '32c', '-set', hint, value])
}
function getHints(xid) {
let value = getHint(xid, UNITE_HINTS)
if (!value) {
value = getHint(xid, MOTIF_HINTS, _SHOW_FLAGS)
setHint(xid, UNITE_HINTS, value)
}
return value
}
function isDecorated(hints) {
return hints[2] != '0x2' && hints[2] != '0x0'
}
var ClientDecorations = class ClientDecorations {
constructor(xid) {
this.xid = xid
}
show() {
return false
}
hide() {
return false
}
reset() {
return false
}
}
var ServerDecorations = class ServerDecorations {
constructor(xid) {
this.xid = xid
this.initial = getHints(xid)
this.current = this.initial
}
get decorated() {
return isDecorated(this.current)
}
get handle() {
return isDecorated(this.initial)
}
show() {
if (this.handle && !this.decorated) {
this.current = _SHOW_FLAGS
setHint(this.xid, MOTIF_HINTS, _SHOW_FLAGS)
}
}
hide() {
if (this.handle && this.decorated) {
this.current = _HIDE_FLAGS
setHint(this.xid, MOTIF_HINTS, _HIDE_FLAGS)
}
}
reset() {
if (this.handle) {
setHint(this.xid, MOTIF_HINTS, this.initial)
}
}
}
var MetaWindow = GObject.registerClass(
class UniteMetaWindow extends GObject.Object {
_init(win) {
win._uniteShellManaged = true
this.win = win
this.xid = getXid(win)
this.signals = new Handlers.Signals()
this.settings = new Handlers.Settings()
if (this.xid && !this.clientDecorated) {
this.decorations = new ServerDecorations(this.xid)
} else {
this.decorations = new ClientDecorations(this.xid)
}
this.signals.connect(
win, 'size-changed', this._onStateChanged.bind(this)
)
this.signals.connect(
win, 'notify::title', this._onTitleChanged.bind(this)
)
this.settings.connect(
'restrict-to-primary-screen', this.syncComponents.bind(this)
)
this.settings.connect(
'hide-window-titlebars', this.syncDecorations.bind(this)
)
this.settings.connect(
'show-window-buttons', this.syncControls.bind(this)
)
this.settings.connect(
'show-window-title', this.syncAppmenu.bind(this)
)
this.syncComponents()
}
get hasFocus() {
return this.win.has_focus()
}
get title() {
if (this.showTitle) {
return this.win.get_title()
} else {
return AppMenu._targetApp.get_name()
}
}
get clientDecorated() {
return this.win.is_client_decorated()
}
get primaryScreen() {
return this.win.is_on_primary_monitor()
}
get minimized() {
return this.win.minimized
}
get maximized() {
return this.win.maximized_horizontally && this.win.maximized_vertically
}
get tiled() {
if (this.maximized) {
return false
} else {
return this.win.maximized_horizontally || this.win.maximized_vertically
}
}
get bothMaximized() {
return this.maximized || this.tiled
}
get restrictToPrimary() {
return this.settings.get('restrict-to-primary-screen')
}
get handleScreen() {
return this.primaryScreen || !this.restrictToPrimary
}
get showTitle() {
return this._parseEnumSetting('show-window-title')
}
get showButtons() {
return this._parseEnumSetting('show-window-buttons')
}
get hideTitlebars() {
return this._parseEnumSetting('hide-window-titlebars')
}
minimize() {
if (this.minimized) {
this.win.unminimize()
} else {
this.win.minimize()
}
}
maximize() {
if (this.maximized) {
this.win.unmaximize(Meta.MaximizeFlags.BOTH)
} else {
this.win.maximize(Meta.MaximizeFlags.BOTH)
}
}
close() {
const time = global.get_current_time()
time && this.win.delete(time)
}
syncDecorations() {
if (this.hideTitlebars) {
this.decorations.hide()
} else {
this.decorations.show()
}
}
syncControls() {
if (this.hasFocus) {
const controls = Main.panel.statusArea.uniteWindowControls
controls && controls.setVisible(this.showButtons)
}
}
syncAppmenu() {
if (this.hasFocus) {
const current = AppMenu._label.get_text()
current != this.title && AppMenu._label.set_text(this.title)
}
}
syncComponents() {
this.syncDecorations()
this.syncControls()
this.syncAppmenu()
}
_parseEnumSetting(name) {
switch (this.settings.get(name)) {
case 'always': return true
case 'never': return false
case 'tiled': return this.handleScreen && this.tiled
case 'maximized': return this.handleScreen && this.maximized
case 'both': return this.handleScreen && this.bothMaximized
}
}
_onStateChanged() {
this.syncComponents()
}
_onTitleChanged() {
this.syncAppmenu()
}
destroy() {
this.decorations.reset()
this.signals.disconnectAll()
this.settings.disconnectAll()
this.win._uniteShellManaged = false
}
}
)
var WindowManager = GObject.registerClass(
class UniteWindowManager extends GObject.Object {
_init() {
this.windows = new Map()
this.signals = new Handlers.Signals()
this.settings = new Handlers.Settings()
this.styles = new Handlers.Styles()
this.signals.connect(
global.window_manager, 'map', this._onMapWindow.bind(this)
)
this.signals.connect(
global.window_manager, 'destroy', this._onDestroyWindow.bind(this)
)
this.signals.connect(
global.display, 'notify::focus-window', this._onFocusWindow.bind(this)
)
this.signals.connect(
global.display, 'window-demands-attention', this._onAttention.bind(this)
)
this.signals.connect(
AppMenu._label, 'notify::text', this._onAppmenuChanged.bind(this)
)
this.settings.connect(
'hide-window-titlebars', this._onStylesChange.bind(this)
)
this.settings.connect(
'window-buttons-position', this._onStylesChange.bind(this)
)
}
get focusWindow() {
const win = global.display.get_focus_window()
return this.getWindow(win)
}
get hideTitlebars() {
return this.settings.get('hide-window-titlebars')
}
hasWindow(win) {
return win && this.windows.has(`${win}`)
}
getWindow(win) {
return win && this.windows.get(`${win}`)
}
setWindow(win) {
if (!this.hasWindow(win)) {
const meta = new MetaWindow(win)
this.windows.set(`${win}`, meta)
}
}
deleteWindow(win) {
if (this.hasWindow(win)) {
const meta = this.getWindow(win)
meta.destroy()
this.windows.delete(`${win}`)
}
}
clearWindows() {
for (const key of this.windows.keys()) {
this.deleteWindow(key)
}
}
_onMapWindow(shellwm, { meta_window }) {
if (isValid(meta_window)) {
this.setWindow(meta_window)
}
}
_onDestroyWindow(shellwm, { meta_window }) {
if (isValid(meta_window)) {
this.deleteWindow(meta_window)
}
}
_onFocusWindow(display) {
if (this.focusWindow) {
this.focusWindow.syncComponents()
}
}
_onAppmenuChanged() {
const focused = this.focusWindow
const current = AppMenu._label.get_text()
if (focused && current != focused.title) {
AppMenu._label.set_text(focused.title)
}
}
_onAttention(actor, win) {
const auto = this.settings.get('autofocus-windows')
const time = global.get_current_time()
auto && Main.activateWindow(win, time)
}
_onStylesChange() {
if (this.hideTitlebars != 'never') {
const variant = this.settings.get('window-buttons-position')
const folder = `${Unite.path}/styles/buttons-${variant}`
const content = `@import url('${folder}/${this.hideTitlebars}.css');`
this.styles.addGtkStyle('windowDecorations', content)
} else {
this.styles.deleteStyle('windowDecorations')
}
}
activate() {
GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
const actors = global.get_window_actors()
actors.forEach(actor => this._onMapWindow(null, actor))
})
this._onStylesChange()
}
destroy() {
this.clearWindows()
this.signals.disconnectAll()
this.settings.disconnectAll()
this.styles.removeAll()
}
}
)