guake-guake/guake/utils.py
2019-04-18 15:15:55 +02:00

358 lines
14 KiB
Python

# -*- coding: utf-8; -*-
"""
Copyright (C) 2018 Mario Aichinger <aichingm@gmail.com>
Copyright (C) 2007-2012 Lincoln de Sousa <lincoln@minaslivre.org>
Copyright (C) 2007 Gabriel Falcão <gabrielteratos@gmail.com>
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 2 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, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301 USA
"""
import datetime
import logging
import os
import platform
import subprocess
import time
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
from gi.repository import Gdk
from gi.repository import GdkX11
from gi.repository import Gtk
from guake.globals import ALIGN_BOTTOM
from guake.globals import ALIGN_CENTER
from guake.globals import ALIGN_LEFT
from guake.globals import ALIGN_RIGHT
from guake.globals import ALIGN_TOP
from guake.globals import ALWAYS_ON_PRIMARY
log = logging.getLogger(__name__)
def get_server_time(widget):
try:
return GdkX11.x11_get_server_time(widget.get_window())
except (TypeError, AttributeError):
# Issue: https://github.com/Guake/guake/issues/1071
# Wayland does not seem to like `x11_get_server_time`.
# Use local timestamp instead
ts = time.time()
return ts
# Decorator for save-tabs-when-changed
def save_tabs_when_changed(func):
"""Decorator for save-tabs-when-changed
"""
def wrapper(*args, **kwargs):
func(*args, **kwargs)
log.debug("mom, I've been called: %s %s", func.__name__, func)
# Find me the Guake!
clsname = args[0].__class__.__name__
g = None
if clsname == 'Guake':
g = args[0]
elif getattr(args[0], 'get_guake', None):
g = args[0].get_guake()
elif getattr(args[0], 'get_notebook', None):
g = args[0].get_notebook().guake
elif getattr(args[0], 'guake', None):
g = args[0].guake
elif getattr(args[0], 'notebook', None):
g = args[0].notebook.guake
# Tada!
if g and g.settings.general.get_boolean('save-tabs-when-changed'):
g.save_tabs()
return wrapper
def save_preferences(filename):
# XXX: Hardcode?
prefs = subprocess.check_output(['dconf', 'dump', '/apps/guake/'])
with open(filename, 'wb') as f:
f.write(prefs)
def restore_preferences(filename):
# XXX: Hardcode?
with open(filename, 'rb') as f:
prefs = f.read()
p = subprocess.Popen(['dconf', 'load', '/apps/guake/'], stdin=subprocess.PIPE)
p.communicate(input=prefs)
class TabNameUtils():
@classmethod
def shorten(cls, text, settings):
use_vte_titles = settings.general.get_boolean('use-vte-titles')
if not use_vte_titles:
return text
max_name_length = settings.general.get_int("max-tab-name-length")
if max_name_length != 0 and len(text) > max_name_length:
text = "..." + text[-max_name_length:]
return text
class HidePrevention():
def __init__(self, window):
"""Create a new HidePrevention object like `HidePrevention(window)`
"""
if not isinstance(window, Gtk.Window):
raise ValueError("window must be of type Gtk.Window, not of type %s" % type(window))
self.window = window
def may_hide(self):
"""returns True if the window is allowed to hide and
False if `prevent()` is called from some where
"""
return getattr(self.window, 'can_hide', True)
def prevent(self):
"""sets a flag on the window object which indicates to
may_hide that the window is NOT allowed to be hidden.
"""
setattr(self.window, 'can_hide', False)
def allow(self):
"""sets the flag so that it indicates to may_hide that the window is allowed to be hidden
"""
setattr(self.window, 'can_hide', True)
class FullscreenManager():
def __init__(self, settings, window):
self.settings = settings
self.window = window
self.is_in_fullscreen = False
def is_fullscreen(self):
return getattr(self.window, 'is_fullscreen', False)
def fullscreen(self):
self.window.fullscreen()
setattr(self.window, 'is_fullscreen', True)
def unfullscreen(self):
setattr(self.window, 'is_fullscreen', False)
self.window.unfullscreen()
# FIX to unfullscreen after show, fullscreen, hide, unfullscreen
# (unfullscreen breaks/does not shrink window size)
RectCalculator.set_final_window_rect(self.settings, self.window)
def toggle(self):
if self.is_fullscreen():
self.unfullscreen()
else:
self.fullscreen()
class RectCalculator():
@classmethod
def set_final_window_rect(cls, settings, window):
"""Sets the final size and location of the main window of guake. The height
is the window_height property, width is window_width and the
horizontal alignment is given by window_alignment.
"""
# fetch settings
height_percents = settings.general.get_int('window-height')
width_percents = settings.general.get_int('window-width')
halignment = settings.general.get_int('window-halignment')
valignment = settings.general.get_int('window-valignment')
vdisplacement = settings.general.get_int('window-vertical-displacement')
hdisplacement = settings.general.get_int('window-horizontal-displacement')
log.debug("set_final_window_rect")
log.debug(" height_percents = %s", height_percents)
log.debug(" width_percents = %s", width_percents)
log.debug(" halignment = %s", halignment)
log.debug(" valignment = %s", valignment)
log.debug(" vdisplacement = %s", vdisplacement)
log.debug(" hdisplacement = %s", hdisplacement)
# get the rectangle just from the destination monitor
screen = window.get_screen()
monitor = cls.get_final_window_monitor(settings, window)
window_rect = screen.get_monitor_geometry(monitor)
log.debug("Current monitor geometry")
log.debug(" window_rect.x: %s", window_rect.x)
log.debug(" window_rect.y: %s", window_rect.y)
log.debug(" window_rect.height: %s", window_rect.height)
log.debug(" window_rect.width: %s", window_rect.width)
log.debug("is unity: %s", cls.is_using_unity(settings, window))
# TODO PORT remove this UNITY is DEAD
if cls.is_using_unity(settings, window):
# For Ubuntu 12.10 and above, try to use dconf:
# see if unity dock is hidden => unity_hide
# and the width of unity dock => unity_dock
# and the position of the unity dock. => unity_pos
# found = False
unity_hide = 0
unity_dock = 0
unity_pos = "Left"
# float() conversion might mess things up. Add 0.01 so the comparison will always be
# valid, even in case of float("10.10") = 10.099999999999999
if float(platform.linux_distribution()[1]) + 0.01 >= 12.10:
try:
unity_hide = int(
subprocess.check_output([
'/usr/bin/dconf', 'read',
'/org/compiz/profiles/unity/plugins/unityshell/launcher-hide-mode'
])
)
unity_dock = int(
subprocess.check_output([
'/usr/bin/dconf', 'read',
'/org/compiz/profiles/unity/plugins/unityshell/icon-size'
]) or "48"
)
unity_pos = subprocess.check_output([
'/usr/bin/dconf', 'read', '/com/canonical/unity/launcher/launcher-position'
]) or "Left"
# found = True
except Exception as e:
# in case of error, just ignore it, 'found' will not be set to True and so
# we execute the fallback
pass
# FIXME: remove self.client dependency
# if not found:
# # Fallback: try to bet from gconf
# unity_hide = self.client.get_int(
# KEY('/apps/compiz-1/plugins/unityshell/screen0/options/launcher_hide_mode')
# )
# unity_icon_size = self.client.get_int(
# KEY('/apps/compiz-1/plugins/unityshell/screen0/options/icon_size')
# )
# unity_dock = unity_icon_size + 17
# launcher_hide_mode = 1 => autohide
# only adjust guake window width if Unity dock is positioned "Left" or "Right"
if unity_hide != 1 and unity_pos not in ("Left", "Right"):
log.debug(
"correcting window width because of launcher position %s "
"and width %s (from %s to %s)", unity_pos, unity_dock, window_rect.width,
window_rect.width - unity_dock
)
window_rect.width = window_rect.width - unity_dock
total_width = window_rect.width
total_height = window_rect.height
log.debug("Correcteed monitor size:")
log.debug(" total_width: %s", total_width)
log.debug(" total_height: %s", total_height)
window_rect.height = int(float(window_rect.height) * float(height_percents) / 100.0)
window_rect.width = int(float(window_rect.width) * float(width_percents) / 100.0)
if window_rect.width < total_width:
if halignment == ALIGN_CENTER:
# log.debug("aligning to center!")
window_rect.x += (total_width - window_rect.width) / 2
elif halignment == ALIGN_LEFT:
# log.debug("aligning to left!")
window_rect.x += 0 + hdisplacement
elif halignment == ALIGN_RIGHT:
# log.debug("aligning to right!")
window_rect.x += total_width - window_rect.width - hdisplacement
if window_rect.height < total_height:
if valignment == ALIGN_BOTTOM:
window_rect.y += (total_height - window_rect.height)
if valignment == ALIGN_TOP:
window_rect.y += vdisplacement
elif valignment == ALIGN_BOTTOM:
window_rect.y -= vdisplacement
if width_percents == 100 and height_percents == 100:
log.debug("MAXIMIZING MAIN WINDOW")
window.maximize()
elif not FullscreenManager(settings, window).is_fullscreen():
log.debug("RESIZING MAIN WINDOW TO THE FOLLOWING VALUES:")
window.unmaximize()
log.debug(" window_rect.x: %s", window_rect.x)
log.debug(" window_rect.y: %s", window_rect.y)
log.debug(" window_rect.height: %s", window_rect.height)
log.debug(" window_rect.width: %s", window_rect.width)
# Note: move_resize is only on GTK3
window.resize(window_rect.width, window_rect.height)
window.move(window_rect.x, window_rect.y)
window.move(window_rect.x, window_rect.y)
log.debug("Updated window position: %r", window.get_position())
return window_rect
@classmethod
def get_final_window_monitor(cls, settings, window):
"""Gets the final screen number for the main window of guake.
"""
screen = window.get_screen()
# fetch settings
use_mouse = settings.general.get_boolean('mouse-display')
dest_screen = settings.general.get_int('display-n')
if use_mouse:
# TODO PORT get_pointer is deprecated
# https://developer.gnome.org/gtk3/stable/GtkWidget.html#gtk-widget-get-pointer
win, x, y, _ = screen.get_root_window().get_pointer()
dest_screen = screen.get_monitor_at_point(x, y)
# If Guake is configured to use a screen that is not currently attached,
# default to 'primary display' option.
n_screens = screen.get_n_monitors()
if dest_screen > n_screens - 1:
settings.general.set_boolean('mouse-display', False)
settings.general.set_int('display-n', dest_screen)
dest_screen = screen.get_primary_monitor()
# Use primary display if configured
if dest_screen == ALWAYS_ON_PRIMARY:
dest_screen = screen.get_primary_monitor()
return dest_screen
# TODO PORT remove this UNITY is DEAD
@classmethod
def is_using_unity(cls, settings, window):
linux_distrib = platform.linux_distribution()
if linux_distrib[0].lower() != "ubuntu":
return False
# http://askubuntu.com/questions/70296/is-there-an-environment-variable-that-is-set-for-unity
if float(linux_distrib[1]) - 0.01 < 11.10:
if os.environ.get('DESKTOP_SESSION', '').lower() == "gnome".lower():
return True
else:
if os.environ.get('XDG_CURRENT_DESKTOP', '').lower() == "unity".lower():
return True
return False