commit
7f415645c8
12 changed files with 612 additions and 624 deletions
4
.pylintrc
Normal file
4
.pylintrc
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
[MAIN]
|
||||
max-line-length=150
|
||||
max-module-lines=2000
|
||||
generated-member=cairo.*
|
||||
|
|
@ -28,7 +28,7 @@ class Autostart:
|
|||
|
||||
def __init__(self, app_name):
|
||||
if not app_name.endswith(".desktop"):
|
||||
app_name = "%s.desktop" % (app_name)
|
||||
app_name = f"{app_name}.desktop"
|
||||
self.app_name = app_name
|
||||
self.auto_locations = [os.path.join(
|
||||
xdg_config_home, 'autostart/'), '/etc/xdg/autostart/']
|
||||
|
|
@ -77,7 +77,7 @@ class BazziteAutostart:
|
|||
|
||||
def __init__(self):
|
||||
self.auto = False
|
||||
with open("/etc/default/discover-overlay") as f:
|
||||
with open("/etc/default/discover-overlay", encoding="utf-8") as f:
|
||||
content = f.readlines()
|
||||
for line in content:
|
||||
if line.startswith("AUTO_LAUNCH_DISCOVER_OVERLAY="):
|
||||
|
|
@ -94,14 +94,15 @@ class BazziteAutostart:
|
|||
self.auto = enable
|
||||
|
||||
def change_file(self, value):
|
||||
"""Alter bazzite config via pkexec and sed"""
|
||||
root = ''
|
||||
if shutil.which('pkexec'):
|
||||
root = 'pkexec'
|
||||
else:
|
||||
log.error("No ability to request root privs. Cancel")
|
||||
return
|
||||
command = " sed -i 's/AUTO_LAUNCH_DISCOVER_OVERLAY=./AUTO_LAUNCH_DISCOVER_OVERLAY=%s/g' /etc/default/discover-overlay" % (
|
||||
value)
|
||||
command = f" sed -i 's/AUTO_LAUNCH_DISCOVER_OVERLAY=./AUTO_LAUNCH_DISCOVER_OVERLAY={
|
||||
value}/g' /etc/default/discover-overlay"
|
||||
command_with_permissions = root + command
|
||||
os.system(command_with_permissions)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,13 +23,11 @@ CHANNEL - Often called 'Rooms'. Both voice and text channels are types of channe
|
|||
import select
|
||||
import time
|
||||
import json
|
||||
import sys
|
||||
import logging
|
||||
import calendar
|
||||
import websocket
|
||||
import requests
|
||||
|
||||
import gi
|
||||
from gi.repository import GLib
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
|
@ -94,9 +92,14 @@ class DiscordConnector:
|
|||
"""
|
||||
url = "https://streamkit.discord.com/overlay/token"
|
||||
myobj = {"code": code1}
|
||||
response = requests.post(url, json=myobj)
|
||||
response = requests.post(url, json=myobj, timeout=10)
|
||||
try:
|
||||
jsonresponse = json.loads(response.text)
|
||||
except requests.exceptions.Timeout:
|
||||
# TODO This probably needs a retry, not a quit
|
||||
jsonresponse = {}
|
||||
except requests.exceptions.TooManyRedirects:
|
||||
jsonresponse = {}
|
||||
except json.JSONDecodeError:
|
||||
jsonresponse = {}
|
||||
if "access_token" in jsonresponse:
|
||||
|
|
@ -106,7 +109,7 @@ class DiscordConnector:
|
|||
log.error("No access token in json response")
|
||||
log.error(response.text)
|
||||
log.error("The user most likely denied permission for this app")
|
||||
sys.exit(1)
|
||||
self.discover.exit()
|
||||
|
||||
def set_channel(self, channel, guild, need_req=True):
|
||||
"""
|
||||
|
|
@ -192,8 +195,7 @@ class DiscordConnector:
|
|||
"""
|
||||
Update a line of text
|
||||
"""
|
||||
for idx in range(0, len(self.text)):
|
||||
message = self.text[idx]
|
||||
for idx, message in enumerate(self.text):
|
||||
if message['id'] == message_in['id']:
|
||||
new_message = {'id': message['id'],
|
||||
'content': self.get_message_from_message(message_in),
|
||||
|
|
@ -209,8 +211,7 @@ class DiscordConnector:
|
|||
"""
|
||||
Delete a line of text
|
||||
"""
|
||||
for idx in range(0, len(self.text)):
|
||||
message = self.text[idx]
|
||||
for idx, message in enumerate(self.text):
|
||||
if message['id'] == message_in['id']:
|
||||
del self.text[idx]
|
||||
self.text_altered = True
|
||||
|
|
@ -278,7 +279,7 @@ class DiscordConnector:
|
|||
self.get_access_token_stage2(j["data"]["code"])
|
||||
else:
|
||||
log.error("Authorization rejected")
|
||||
sys.exit(0)
|
||||
self.discover.exit()
|
||||
return
|
||||
elif j["cmd"] == "DISPATCH":
|
||||
if j["evt"] == "READY":
|
||||
|
|
@ -306,6 +307,7 @@ class DiscordConnector:
|
|||
# We've joined a room... but where?
|
||||
if j["data"]["user"]["id"] == self.user["id"]:
|
||||
self.find_user()
|
||||
self.userlist[thisuser["id"]]["lastspoken"] = time.perf_counter()
|
||||
elif j["evt"] == "VOICE_STATE_DELETE":
|
||||
self.list_altered = True
|
||||
self.set_in_room(j["data"]["user"]["id"], False)
|
||||
|
|
@ -318,7 +320,7 @@ class DiscordConnector:
|
|||
elif j["evt"] == "SPEAKING_START":
|
||||
self.list_altered = True
|
||||
self.userlist[j["data"]["user_id"]]["speaking"] = True
|
||||
self.userlist[j["data"]["user_id"]]["lastspoken"] = time.time()
|
||||
self.userlist[j["data"]["user_id"]]["lastspoken"] = time.perf_counter()
|
||||
self.set_in_room(j["data"]["user_id"], True)
|
||||
elif j["evt"] == "SPEAKING_STOP":
|
||||
self.list_altered = True
|
||||
|
|
@ -386,7 +388,8 @@ class DiscordConnector:
|
|||
self.dump_channel_data()
|
||||
return
|
||||
elif j["cmd"] == "GET_GUILD":
|
||||
# We currently only get here because of a "CHANNEL_CREATE" event. Stupidly long winded way around
|
||||
# We currently only get here because of a "CHANNEL_CREATE" event.
|
||||
# Stupidly long winded way around
|
||||
if j["data"]:
|
||||
guild = j["data"]
|
||||
self.dump_channel_data()
|
||||
|
|
@ -417,7 +420,8 @@ class DiscordConnector:
|
|||
self.set_channel(j['data']['id'], j['data']['guild_id'])
|
||||
self.discover.voice_overlay.set_channel_title(
|
||||
j["data"]["name"])
|
||||
if self.current_guild in self.guilds and 'icon_url' in self.guilds[self.current_guild]:
|
||||
if (self.current_guild in self.guilds and
|
||||
'icon_url' in self.guilds[self.current_guild]):
|
||||
self.discover.voice_overlay.set_channel_icon(
|
||||
self.guilds[self.current_guild]['icon_url'])
|
||||
else:
|
||||
|
|
@ -460,7 +464,8 @@ class DiscordConnector:
|
|||
log.warning(j)
|
||||
|
||||
def dump_channel_data(self):
|
||||
with open(self.discover.channel_file, 'w') as f:
|
||||
""" Write all channel data out to file"""
|
||||
with open(self.discover.channel_file, 'w', encoding="utf-8") as f:
|
||||
f.write(json.dumps(
|
||||
{'channels': self.channels, 'guild': self.guilds}))
|
||||
|
||||
|
|
@ -536,7 +541,7 @@ class DiscordConnector:
|
|||
if guild in self.guilds:
|
||||
self.rate_limited_channels.append(guild)
|
||||
else:
|
||||
log.warning(f"Didn't find guild with id {guild}")
|
||||
log.warning("Didn't find guild with id %s", guild)
|
||||
|
||||
def req_channel_details(self, channel, nonce=None):
|
||||
"""message
|
||||
|
|
@ -669,6 +674,7 @@ class DiscordConnector:
|
|||
self.websocket.send(json.dumps(cmd))
|
||||
|
||||
def set_mute(self, muted):
|
||||
""" Set client muted status """
|
||||
cmd = {
|
||||
"cmd": "SET_VOICE_SETTINGS",
|
||||
"args": {"mute": muted},
|
||||
|
|
@ -679,6 +685,7 @@ class DiscordConnector:
|
|||
return False
|
||||
|
||||
def set_deaf(self, deaf):
|
||||
""" Set client deafened status """
|
||||
cmd = {
|
||||
"cmd": "SET_VOICE_SETTINGS",
|
||||
"args": {"deaf": deaf},
|
||||
|
|
@ -688,14 +695,14 @@ class DiscordConnector:
|
|||
self.websocket.send(json.dumps(cmd))
|
||||
return False
|
||||
|
||||
def change_voice_room(self, id):
|
||||
def change_voice_room(self, room_id):
|
||||
"""
|
||||
Switch to another voice room
|
||||
"""
|
||||
cmd = {
|
||||
"cmd": "SELECT_VOICE_CHANNEL",
|
||||
"args": {
|
||||
"channel_id": id,
|
||||
"channel_id": room_id,
|
||||
"force": True
|
||||
},
|
||||
"nonce": "deadbeef"
|
||||
|
|
@ -703,14 +710,14 @@ class DiscordConnector:
|
|||
if self.websocket:
|
||||
self.websocket.send(json.dumps(cmd))
|
||||
|
||||
def change_text_room(self, id):
|
||||
def change_text_room(self, room_id):
|
||||
"""
|
||||
Switch to another text room
|
||||
"""
|
||||
cmd = {
|
||||
"cmd": "SELECT_TEXT_CHANNEL",
|
||||
"args": {
|
||||
"channel_id": id
|
||||
"channel_id": room_id
|
||||
},
|
||||
"nonce": "deadbeef"
|
||||
}
|
||||
|
|
@ -718,7 +725,8 @@ class DiscordConnector:
|
|||
self.websocket.send(json.dumps(cmd))
|
||||
|
||||
def update_overlays_from_data(self):
|
||||
if self.websocket == None:
|
||||
"""Send new data out to overlay windows"""
|
||||
if self.websocket is None:
|
||||
self.discover.voice_overlay.set_blank()
|
||||
if self.discover.text_overlay:
|
||||
self.discover.text_overlay.set_blank()
|
||||
|
|
@ -773,12 +781,13 @@ class DiscordConnector:
|
|||
|
||||
This will be mixed in with 'None' in the list where a voice channel is
|
||||
"""
|
||||
if (guild_id == 0):
|
||||
if guild_id == 0:
|
||||
return
|
||||
self.rate_limited_channels.append(guild_id)
|
||||
|
||||
def schedule_reconnect(self):
|
||||
if self.reconnect_cb == None:
|
||||
"""Set a timer to attempt reconnection"""
|
||||
if self.reconnect_cb is None:
|
||||
log.info("Scheduled a reconnect")
|
||||
self.reconnect_cb = GLib.timeout_add_seconds(60, self.connect)
|
||||
else:
|
||||
|
|
@ -792,25 +801,30 @@ class DiscordConnector:
|
|||
"""
|
||||
log.info("Connecting...")
|
||||
if self.websocket:
|
||||
log.warn("Already connected?")
|
||||
log.warning("Already connected?")
|
||||
return
|
||||
if self.reconnect_cb:
|
||||
GLib.source_remove(self.reconnect_cb)
|
||||
self.reconnect_cb = None
|
||||
try:
|
||||
self.websocket = websocket.create_connection(
|
||||
"ws://127.0.0.1:6463/?v=1&client_id=%s" % (self.oauth_token),
|
||||
f"ws://127.0.0.1:6463/?v=1&client_id={self.oauth_token}",
|
||||
origin="http://localhost:3000",
|
||||
timeout=0.1
|
||||
)
|
||||
if self.socket_watch:
|
||||
GLib.source_remove(self.socket_watch)
|
||||
self.socket_watch = GLib.io_add_watch(
|
||||
self.websocket.sock, GLib.PRIORITY_DEFAULT_IDLE, GLib.IOCondition.HUP | GLib.IOCondition.IN | GLib.IOCondition.ERR, self.socket_glib)
|
||||
except ConnectionError as error:
|
||||
self.websocket.sock,
|
||||
GLib.PRIORITY_DEFAULT_IDLE,
|
||||
GLib.IOCondition.HUP | GLib.IOCondition.IN | GLib.IOCondition.ERR,
|
||||
self.socket_glib
|
||||
)
|
||||
except ConnectionError as _error:
|
||||
self.schedule_reconnect()
|
||||
|
||||
def socket_glib(self, fd, condition):
|
||||
def socket_glib(self, _fd, condition):
|
||||
"""Handle new data on socket"""
|
||||
if condition == GLib.IO_IN and self.websocket:
|
||||
recv, _w, _e = select.select((self.websocket.sock,), (), (), 0)
|
||||
while recv:
|
||||
|
|
|
|||
|
|
@ -13,16 +13,15 @@
|
|||
"""Main application class"""
|
||||
import gettext
|
||||
import os
|
||||
import time
|
||||
import sys
|
||||
import re
|
||||
import traceback
|
||||
import logging
|
||||
import pkg_resources
|
||||
import json
|
||||
import signal
|
||||
import gi
|
||||
from configparser import ConfigParser
|
||||
import gi
|
||||
import pkg_resources
|
||||
|
||||
from .settings_window import MainSettingsWindow
|
||||
from .voice_overlay import VoiceOverlayWindow
|
||||
|
|
@ -33,7 +32,7 @@ from .audio_assist import DiscoverAudioAssist
|
|||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
# pylint: disable=wrong-import-position,wrong-import-order
|
||||
from gi.repository import Gtk, GLib, Gio, Gdk # nopep8
|
||||
from gi.repository import Gtk, GLib, Gio # nopep8
|
||||
|
||||
try:
|
||||
from xdg.BaseDirectory import xdg_config_home
|
||||
|
|
@ -82,8 +81,8 @@ class Discover:
|
|||
self.connection.connect()
|
||||
self.audio_assist = DiscoverAudioAssist(self)
|
||||
|
||||
rpc_file = Gio.File.new_for_path(rpc_file)
|
||||
monitor = rpc_file.monitor_file(0, None)
|
||||
rpc_file_gio = Gio.File.new_for_path(rpc_file)
|
||||
monitor = rpc_file_gio.monitor_file(0, None)
|
||||
monitor.connect("changed", self.rpc_changed)
|
||||
|
||||
config_file = Gio.File.new_for_path(config_file)
|
||||
|
|
@ -99,7 +98,7 @@ class Discover:
|
|||
Read in arg list from command or RPC and act accordingly
|
||||
"""
|
||||
if "--help" in data or "-h" in data:
|
||||
print("%s: discover-overlay [OPTIONS]... " % (_("Usage")))
|
||||
print(f"{_("Usage")}: discover-overlay [OPTIONS]... ")
|
||||
print(_("Show an X11 or wlroots overlay with information"))
|
||||
print(_("from Discord client"))
|
||||
print("")
|
||||
|
|
@ -126,7 +125,7 @@ class Discover:
|
|||
if normal_close:
|
||||
sys.exit(0)
|
||||
if "--close" in data or "-x" in data:
|
||||
sys.exit(0)
|
||||
self.exit()
|
||||
if "--steamos" in data or "-s" in data:
|
||||
self.steamos = True
|
||||
if "--hide" in data:
|
||||
|
|
@ -157,15 +156,20 @@ class Discover:
|
|||
if self.connection:
|
||||
self.connection.request_text_rooms_for_guild(match.group(1))
|
||||
|
||||
def exit(self):
|
||||
os.kill(os.getpid(), signal.SIGTERM)
|
||||
|
||||
def config_set(self, context, key, value):
|
||||
"""Set a config value and save to disk"""
|
||||
config = self.config()
|
||||
if not context in config.sections():
|
||||
config.add_section(context)
|
||||
config.set(context, key, value)
|
||||
with open(self.config_file, 'w') as file:
|
||||
with open(self.config_file, 'w', encoding="utf-8") as file:
|
||||
config.write(file)
|
||||
|
||||
def config(self):
|
||||
"""Read config from disk"""
|
||||
config = ConfigParser(interpolation=None)
|
||||
config.read(self.config_file)
|
||||
return config
|
||||
|
|
@ -174,7 +178,7 @@ class Discover:
|
|||
"""
|
||||
Called when the RPC file has been altered
|
||||
"""
|
||||
with open(self.rpc_file, "r") as tfile:
|
||||
with open(self.rpc_file, "r", encoding="utf-8") as tfile:
|
||||
data = tfile.readlines()
|
||||
if len(data) >= 1:
|
||||
self.do_args(data[0].strip().split(" "), False)
|
||||
|
|
@ -249,9 +253,7 @@ class Discover:
|
|||
|
||||
self.voice_overlay.set_horizontal(config.getboolean(
|
||||
"main", "horizontal", fallback=False))
|
||||
self.voice_overlay.set_guild_ids(self.parse_guild_ids(
|
||||
config.get("main", "guild_ids", fallback="")))
|
||||
self.voice_overlay.set_overflow(
|
||||
self.voice_overlay.set_overflow_style(
|
||||
config.getint("main", "overflow", fallback=0))
|
||||
self.voice_overlay.set_show_connection(config.getboolean(
|
||||
"main", "show_connection", fallback=False))
|
||||
|
|
@ -259,7 +261,7 @@ class Discover:
|
|||
"main", "show_title", fallback=False))
|
||||
self.voice_overlay.set_show_disconnected(config.getboolean(
|
||||
"main", "show_disconnected", fallback=False))
|
||||
self.voice_overlay.set_border_width(
|
||||
self.voice_overlay.set_drawn_border_width(
|
||||
config.getint("main", "border_width", fallback=2))
|
||||
self.voice_overlay.set_icon_transparency(config.getfloat(
|
||||
"main", "icon_transparency", fallback=1.0))
|
||||
|
|
@ -432,6 +434,7 @@ class Discover:
|
|||
self.config_file, self.rpc_file, self.channel_file, [])
|
||||
|
||||
def toggle_show(self, _obj=None):
|
||||
"""Toggle all overlays off or on"""
|
||||
if self.voice_overlay:
|
||||
hide = not self.voice_overlay.hidden
|
||||
self.voice_overlay.set_hidden(hide)
|
||||
|
|
@ -457,6 +460,8 @@ class Discover:
|
|||
self.notification_overlay.set_force_xshape(force)
|
||||
|
||||
def set_show_task(self, visible):
|
||||
"""Set if the overlay should allow itself to appear on taskbar.
|
||||
Not working at last check"""
|
||||
if self.voice_overlay:
|
||||
self.voice_overlay.set_task(visible)
|
||||
if self.text_overlay:
|
||||
|
|
@ -465,11 +470,13 @@ class Discover:
|
|||
self.notification_overlay.set_task(visible)
|
||||
|
||||
def set_mute_async(self, mute):
|
||||
if mute != None:
|
||||
"""Set mute status from another thread"""
|
||||
if mute is not None:
|
||||
GLib.idle_add(self.connection.set_mute, mute)
|
||||
|
||||
def set_deaf_async(self, deaf):
|
||||
if deaf != None:
|
||||
"""Set deaf status from another thread"""
|
||||
if deaf is not None:
|
||||
GLib.idle_add(self.connection.set_deaf, deaf)
|
||||
|
||||
|
||||
|
|
@ -499,13 +506,12 @@ def entrypoint():
|
|||
|
||||
# Prepare logger
|
||||
logging.getLogger().setLevel(logging.INFO)
|
||||
FORMAT = "%(levelname)s - %(name)s - %(message)s"
|
||||
log_format = "%(levelname)s - %(name)s - %(message)s"
|
||||
if "--debug" in sys.argv or "-v" in sys.argv:
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
logging.basicConfig(filename=debug_file, format=FORMAT)
|
||||
logging.basicConfig(filename=debug_file, format=log_format)
|
||||
else:
|
||||
logging.basicConfig(format=FORMAT)
|
||||
log = logging.getLogger(__name__)
|
||||
logging.basicConfig(format=log_format)
|
||||
log.info("Starting Discover Overlay: %s",
|
||||
pkg_resources.get_distribution('discover_overlay').version)
|
||||
|
||||
|
|
@ -520,26 +526,26 @@ def entrypoint():
|
|||
# Send command to overlay
|
||||
line = ""
|
||||
for arg in sys.argv[1:]:
|
||||
line = "%s %s" % (line, arg)
|
||||
with open(rpc_file, "w") as tfile:
|
||||
line = f"{line} {arg}"
|
||||
with open(rpc_file, "w", encoding="utf-8") as tfile:
|
||||
tfile.write(line)
|
||||
log.warning("Sent RPC command")
|
||||
else:
|
||||
if "-c" in sys.argv or "--configure" in sys.argv:
|
||||
# Show config window
|
||||
settings = MainSettingsWindow(
|
||||
_settings = MainSettingsWindow(
|
||||
config_file, rpc_file, channel_file, sys.argv[1:])
|
||||
Gtk.main()
|
||||
else:
|
||||
# Tell any other running overlay to close
|
||||
with open(rpc_file, "w") as tfile:
|
||||
with open(rpc_file, "w", encoding="utf-8") as tfile:
|
||||
tfile.write("--close")
|
||||
# Show the overlay
|
||||
Discover(rpc_file, config_file, channel_file,
|
||||
debug_file, sys.argv[1:])
|
||||
return
|
||||
|
||||
except Exception as ex:
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
log.error(ex)
|
||||
log.error(traceback.format_exc())
|
||||
sys.exit(1)
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""An X11 window which can be moved and resized"""
|
||||
import logging
|
||||
import gi
|
||||
import cairo
|
||||
import logging
|
||||
gi.require_version("Gtk", "3.0")
|
||||
# pylint: disable=wrong-import-position
|
||||
from gi.repository import Gtk, Gdk # nopep8
|
||||
|
|
@ -24,10 +24,12 @@ log = logging.getLogger(__name__)
|
|||
class DraggableWindow(Gtk.Window):
|
||||
"""An X11 window which can be moved and resized"""
|
||||
|
||||
def __init__(self, pos_x=0.0, pos_y=0.0, width=0.1, height=0.1, message="Message", settings=None, monitor=None):
|
||||
def __init__(self, pos_x=0.0, pos_y=0.0, width=0.1, height=0.1,
|
||||
message="Message", settings=None, monitor=None):
|
||||
Gtk.Window.__init__(self, type=Gtk.WindowType.POPUP)
|
||||
self.monitor = monitor
|
||||
(screen_x, screen_y, screen_width, screen_height) = self.get_display_coords()
|
||||
(_screen_x, _screen_y, screen_width,
|
||||
screen_height) = self.get_display_coords()
|
||||
self.pos_x = pos_x * screen_width
|
||||
self.pos_y = pos_y * screen_height
|
||||
self.width = max(40, width * screen_width)
|
||||
|
|
@ -84,8 +86,8 @@ class DraggableWindow(Gtk.Window):
|
|||
if event.state & Gdk.ModifierType.BUTTON1_MASK:
|
||||
if self.drag_type == 1:
|
||||
# Center is move
|
||||
(screen_x, screen_y, screen_width,
|
||||
screen_height) = self.get_display_coords()
|
||||
(screen_x, screen_y, _screen_width,
|
||||
_screen_height) = self.get_display_coords()
|
||||
self.pos_x = (event.x_root - screen_x) - self.drag_x
|
||||
self.pos_y = (event.y_root - screen_y) - self.drag_y
|
||||
self.force_location()
|
||||
|
|
@ -151,6 +153,7 @@ class DraggableWindow(Gtk.Window):
|
|||
context.fill()
|
||||
|
||||
def get_display_coords(self):
|
||||
"""Get coordinates for this display"""
|
||||
display = Gdk.Display.get_default()
|
||||
if "get_monitor" in dir(display):
|
||||
monitor = display.get_monitor(self.monitor)
|
||||
|
|
@ -171,4 +174,5 @@ class DraggableWindow(Gtk.Window):
|
|||
height = float(height)
|
||||
pos_x = pos_x / scale
|
||||
pos_y = pos_y / scale
|
||||
return (pos_x / screen_width, pos_y / screen_height, width / screen_width, height / screen_height)
|
||||
return (pos_x / screen_width, pos_y / screen_height,
|
||||
width / screen_width, height / screen_height)
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""A Wayland full-screen window which can be moved and resized"""
|
||||
import logging
|
||||
import cairo
|
||||
import gi
|
||||
import logging
|
||||
gi.require_version("Gtk", "3.0")
|
||||
# pylint: disable=wrong-import-position
|
||||
from gi.repository import Gtk, Gdk # nopep8
|
||||
|
|
@ -29,12 +29,14 @@ log = logging.getLogger(__name__)
|
|||
class DraggableWindowWayland(Gtk.Window):
|
||||
"""A Wayland full-screen window which can be moved and resized"""
|
||||
|
||||
def __init__(self, pos_x=0.0, pos_y=0.0, width=0.1, height=0.1, message="Message", settings=None, steamos=False, monitor=None):
|
||||
def __init__(self, pos_x=0.0, pos_y=0.0, width=0.1, height=0.1,
|
||||
message="Message", settings=None, steamos=False, monitor=None):
|
||||
Gtk.Window.__init__(self, type=Gtk.WindowType.TOPLEVEL)
|
||||
if steamos:
|
||||
monitor = 0
|
||||
self.monitor = monitor
|
||||
(screen_x, screen_y, screen_width, screen_height) = self.get_display_coords()
|
||||
(_screen_x, _screen_y, screen_width,
|
||||
screen_height) = self.get_display_coords()
|
||||
self.pos_x = pos_x * screen_width
|
||||
self.pos_y = pos_y * screen_height
|
||||
self.width = max(40, width * screen_width)
|
||||
|
|
@ -48,8 +50,8 @@ class DraggableWindowWayland(Gtk.Window):
|
|||
self.connect('button-press-event', self.button_press)
|
||||
self.connect('button-release-event', self.button_release)
|
||||
|
||||
log.info("Starting: %d,%d %d x %d" %
|
||||
(self.pos_x, self.pos_y, self.width, self.height))
|
||||
log.info("Starting: %d,%d %d x %d",
|
||||
self.pos_x, self.pos_y, self.width, self.height)
|
||||
|
||||
self.set_app_paintable(True)
|
||||
|
||||
|
|
@ -76,21 +78,24 @@ class DraggableWindowWayland(Gtk.Window):
|
|||
self.force_location()
|
||||
|
||||
def set_steamos_window_size(self):
|
||||
"""Prepare window for a gamescope steamos session"""
|
||||
# Huge bunch of assumptions.
|
||||
# Gamescope only has one monitor
|
||||
# Gamescope has no scale factor
|
||||
# Probably never possible to reach here, as Gamescope/SteamOS
|
||||
# is X11 for overlays
|
||||
display = Gdk.Display.get_default()
|
||||
if "get_monitor" in dir(display):
|
||||
monitor = display.get_monitor(0)
|
||||
if monitor:
|
||||
geometry = monitor.get_geometry()
|
||||
scale_factor = monitor.get_scale_factor()
|
||||
log.info("%d %d" % (geometry.width, geometry.height))
|
||||
log.info("%d %d", geometry.width, geometry.height)
|
||||
self.set_size_request(geometry.width, geometry.height)
|
||||
|
||||
def force_location(self):
|
||||
"""Move the window to previously given co-ords. In wayland just clip to current screen"""
|
||||
(screen_x, screen_y, screen_width, screen_height) = self.get_display_coords()
|
||||
(_screen_x, _screen_y, screen_width,
|
||||
screen_height) = self.get_display_coords()
|
||||
self.width = min(self.width, screen_width)
|
||||
self.height = min(self.height, screen_height)
|
||||
self.pos_x = max(0, self.pos_x)
|
||||
|
|
@ -189,6 +194,7 @@ class DraggableWindowWayland(Gtk.Window):
|
|||
context.restore()
|
||||
|
||||
def get_display_coords(self):
|
||||
"""Get coordinates from display"""
|
||||
display = Gdk.Display.get_default()
|
||||
if "get_monitor" in dir(display):
|
||||
monitor = display.get_monitor(self.monitor)
|
||||
|
|
@ -199,5 +205,7 @@ class DraggableWindowWayland(Gtk.Window):
|
|||
|
||||
def get_coords(self):
|
||||
"""Return the position and size of the window"""
|
||||
(screen_x, screen_y, screen_width, screen_height) = self.get_display_coords()
|
||||
return (float(self.pos_x) / screen_width, float(self.pos_y) / screen_height, float(self.width) / screen_width, float(self.height) / screen_height)
|
||||
(_screen_x, _screen_y, screen_width,
|
||||
screen_height) = self.get_display_coords()
|
||||
return (float(self.pos_x) / screen_width, float(self.pos_y) / screen_height,
|
||||
float(self.width) / screen_width, float(self.height) / screen_height)
|
||||
|
|
|
|||
|
|
@ -11,21 +11,19 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""Functions & Classes to assist image loading."""
|
||||
import urllib
|
||||
import threading
|
||||
import logging
|
||||
import os
|
||||
import copy
|
||||
import gi
|
||||
import requests
|
||||
import cairo
|
||||
import PIL
|
||||
import PIL.Image as Image
|
||||
import os
|
||||
import io
|
||||
import copy
|
||||
gi.require_version('GdkPixbuf', '2.0')
|
||||
gi.require_version("Gtk", "3.0")
|
||||
# pylint: disable=wrong-import-position
|
||||
from gi.repository import Gio, GdkPixbuf, Gtk # nopep8
|
||||
from gi.repository import Gtk # nopep8
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -43,7 +41,7 @@ class SurfaceGetter():
|
|||
"""Downloads and decodes"""
|
||||
try:
|
||||
resp = requests.get(
|
||||
self.url, stream=True, headers={
|
||||
self.url, stream=True, timeout=10, headers={
|
||||
'Referer': 'https://streamkit.discord.com/overlay/voice',
|
||||
'User-Agent': 'Mozilla/5.0'
|
||||
}
|
||||
|
|
@ -69,6 +67,7 @@ class SurfaceGetter():
|
|||
log.error("Unknown image type: %s", self.url)
|
||||
|
||||
def get_file(self):
|
||||
"""Attempt to load the file"""
|
||||
errors = []
|
||||
# Grab icon from icon theme
|
||||
icon_theme = Gtk.IconTheme.get_default()
|
||||
|
|
@ -99,13 +98,13 @@ class SurfaceGetter():
|
|||
try:
|
||||
image = Image.open(mixpath)
|
||||
except ValueError:
|
||||
errors.append("Value Error - Unable to read %s" % (mixpath))
|
||||
errors.append(f"Value Error - Unable to read {mixpath}")
|
||||
except TypeError:
|
||||
errors.append("Type Error - Unable to read %s" % (mixpath))
|
||||
errors.append(f"Type Error - Unable to read {mixpath}")
|
||||
except PIL.UnidentifiedImageError:
|
||||
errors.append("Unknown image type: %s" % (mixpath))
|
||||
errors.append(f"Unknown image type: {mixpath}")
|
||||
except FileNotFoundError:
|
||||
errors.append("File not found: %s" % (mixpath))
|
||||
errors.append(f"File not found: {mixpath}")
|
||||
if image:
|
||||
(surface, mask) = from_pil(image)
|
||||
if surface:
|
||||
|
|
@ -115,7 +114,7 @@ class SurfaceGetter():
|
|||
log.error(error)
|
||||
|
||||
|
||||
def from_pil(image, alpha=1.0, format='BGRa'):
|
||||
def from_pil(image, alpha=1.0, image_format='BGRa'):
|
||||
"""
|
||||
:param im: Pillow Image
|
||||
:param alpha: 0..1 alpha to add to non-alpha images
|
||||
|
|
@ -125,10 +124,10 @@ def from_pil(image, alpha=1.0, format='BGRa'):
|
|||
mask = bytearray()
|
||||
if 'A' not in image.getbands():
|
||||
image.putalpha(int(alpha * 255.0))
|
||||
arr = bytearray(image.tobytes('raw', format))
|
||||
arr = bytearray(image.tobytes('raw', image_format))
|
||||
mask = arr
|
||||
else:
|
||||
arr = bytearray(image.tobytes('raw', format))
|
||||
arr = bytearray(image.tobytes('raw', image_format))
|
||||
mask = copy.deepcopy((arr))
|
||||
idx = 0
|
||||
while idx < len(arr):
|
||||
|
|
@ -148,9 +147,12 @@ def from_pil(image, alpha=1.0, format='BGRa'):
|
|||
|
||||
|
||||
def to_pil(surface):
|
||||
"""Return a PIL Image from the Cairo surface"""
|
||||
if surface.get_format() == cairo.Format.ARGB32:
|
||||
return Image.frombuffer('RGBA', (surface.get_width(), surface.get_height()), surface.get_data(), 'raw', "BGRA", surface.get_stride())
|
||||
return Image.frombuffer("RGB", (surface.get_width(), surface.get_height()), surface.get_data(), 'raw', "BGRX", surface.get_stride())
|
||||
return Image.frombuffer('RGBA', (surface.get_width(), surface.get_height()),
|
||||
surface.get_data(), 'raw', "BGRA", surface.get_stride())
|
||||
return Image.frombuffer("RGB", (surface.get_width(), surface.get_height()),
|
||||
surface.get_data(), 'raw', "BGRX", surface.get_stride())
|
||||
|
||||
|
||||
def get_surface(func, identifier, ava, size):
|
||||
|
|
|
|||
|
|
@ -13,16 +13,15 @@
|
|||
"""Notification window for text"""
|
||||
import logging
|
||||
import time
|
||||
import re
|
||||
import cairo
|
||||
import math
|
||||
|
||||
import cairo
|
||||
import gi
|
||||
from .image_getter import get_surface, draw_img_to_rect, get_aspected_size
|
||||
from .image_getter import get_surface, draw_img_to_rect
|
||||
from .overlay import OverlayWindow
|
||||
gi.require_version("Gtk", "3.0")
|
||||
gi.require_version('PangoCairo', '1.0')
|
||||
# pylint: disable=wrong-import-position,wrong-import-order
|
||||
from gi.repository import Gtk, Pango, PangoCairo # nopep8
|
||||
from gi.repository import Pango, PangoCairo # nopep8
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -34,12 +33,54 @@ class NotificationOverlayWindow(OverlayWindow):
|
|||
OverlayWindow.__init__(self, discover, piggyback)
|
||||
self.text_spacing = 4
|
||||
self.content = []
|
||||
self.test_content = [{"icon": "https://cdn.discordapp.com/icons/951077080769114172/991abffc0d2a5c040444be4d1a4085f4.webp?size=96", "title": "Title1"},
|
||||
{"title": "Title2", "body": "Body", "icon": None},
|
||||
{"icon": "https://cdn.discordapp.com/icons/951077080769114172/991abffc0d2a5c040444be4d1a4085f4.webp?size=96", "title": "Title 3",
|
||||
"body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."},
|
||||
{"icon": None, "title": "Title 3", "body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."},
|
||||
{"icon": "https://cdn.discordapp.com/avatars/147077941317206016/6a6935192076489fa6dc1eb5dafbf6e7.webp?size=128", "title": "PM", "body": "Birdy test"}]
|
||||
self.test_content = [
|
||||
{
|
||||
"icon": (
|
||||
"https://cdn.discordapp.com/"
|
||||
"icons/951077080769114172/991abffc0d2a5c040444be4d1a4085f4.webp?size=96"
|
||||
),
|
||||
"title": "Title1"
|
||||
},
|
||||
{
|
||||
"title": "Title2",
|
||||
"body": "Body",
|
||||
"icon": None
|
||||
},
|
||||
{
|
||||
"icon": ("https://cdn.discordapp.com/"
|
||||
"icons/951077080769114172/991abffc0d2a5c040444be4d1a4085f4.webp?size=96"
|
||||
),
|
||||
"title": "Title 3",
|
||||
"body": ("Lorem ipsum dolor sit amet, consectetur adipiscing elit,"
|
||||
" sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "
|
||||
"Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris "
|
||||
"nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in "
|
||||
"reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla "
|
||||
"pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa "
|
||||
"qui officia deserunt mollit anim id est laborum."
|
||||
)
|
||||
},
|
||||
{
|
||||
"icon": None,
|
||||
"title": "Title 3",
|
||||
"body": ("Lorem ipsum dolor sit amet, consectetur adipiscing elit, "
|
||||
"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "
|
||||
"Ut enim ad minim veniam, quis nostrud exercitation ullamco "
|
||||
"laboris nisi ut aliquip ex ea commodo consequat. Duis aute "
|
||||
"irure dolor in reprehenderit in voluptate velit esse cillum "
|
||||
"dolore eu fugiat nulla pariatur. Excepteur sint occaecat "
|
||||
"cupidatat non proident, sunt in culpa qui officia deserunt "
|
||||
"mollit anim id est laborum."
|
||||
)
|
||||
},
|
||||
{
|
||||
"icon": ("https://cdn.discordapp.com/"
|
||||
"avatars/147077941317206016/6a6935192076489fa6dc1eb5dafbf6e7.webp?size=128"
|
||||
),
|
||||
"title": "PM",
|
||||
"body": "Birdy test"
|
||||
}
|
||||
]
|
||||
self.text_font = None
|
||||
self.text_size = 13
|
||||
self.text_time = None
|
||||
|
|
@ -67,11 +108,12 @@ class NotificationOverlayWindow(OverlayWindow):
|
|||
self.redraw()
|
||||
|
||||
def set_blank(self):
|
||||
"""Set to no data and redraw"""
|
||||
self.content = []
|
||||
self.set_needs_redraw()
|
||||
|
||||
def tick(self):
|
||||
# This doesn't really belong in overlay or settings
|
||||
"""Remove old messages from dataset"""
|
||||
now = time.time()
|
||||
newlist = []
|
||||
oldsize = len(self.content)
|
||||
|
|
@ -85,6 +127,7 @@ class NotificationOverlayWindow(OverlayWindow):
|
|||
self.set_needs_redraw()
|
||||
|
||||
def add_notification_message(self, data):
|
||||
"""Add new message to dataset"""
|
||||
noti = None
|
||||
data = data['data']
|
||||
message_id = data['message']['id']
|
||||
|
|
@ -108,59 +151,50 @@ class NotificationOverlayWindow(OverlayWindow):
|
|||
self.get_all_images()
|
||||
|
||||
def set_padding(self, padding):
|
||||
"""
|
||||
Set the padding between notifications
|
||||
"""
|
||||
"""Config option: Padding between notifications, in window-space pixels"""
|
||||
if self.padding != padding:
|
||||
self.padding = padding
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_border_radius(self, radius):
|
||||
"""
|
||||
Set the radius of the border
|
||||
"""
|
||||
"""Config option: Radius of the border, in window-space pixels"""
|
||||
if self.border_radius != radius:
|
||||
self.border_radius = radius
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_icon_size(self, size):
|
||||
"""
|
||||
Set Icon size
|
||||
"""
|
||||
"""Config option: Size of icons, in window-space pixels"""
|
||||
if self.icon_size != size:
|
||||
self.icon_size = size
|
||||
self.image_list = {}
|
||||
self.get_all_images()
|
||||
|
||||
def set_icon_pad(self, pad):
|
||||
"""
|
||||
Set padding between icon and message
|
||||
"""
|
||||
"""Config option: Padding between icon and message, in window-space pixels"""
|
||||
if self.icon_pad != pad:
|
||||
self.icon_pad = pad
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_icon_left(self, left):
|
||||
"""Config option: Icon on left or right of text"""
|
||||
if self.icon_left != left:
|
||||
self.icon_left = left
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_text_time(self, timer):
|
||||
"""
|
||||
Set the duration that a message will be visible for.
|
||||
"""
|
||||
"""Config option: Duration that a message will be visible for, in seconds"""
|
||||
self.text_time = timer
|
||||
self.timer_after_draw = timer
|
||||
|
||||
def set_limit_width(self, limit):
|
||||
"""
|
||||
Set the word wrap limit in pixels
|
||||
"""Config option: Word wrap limit, in window-space pixels
|
||||
"""
|
||||
if self.limit_width != limit:
|
||||
self.limit_width = limit
|
||||
self.set_needs_redraw()
|
||||
|
||||
def get_all_images(self):
|
||||
"""Return a list of all downloaded images"""
|
||||
the_list = self.content
|
||||
if self.testing:
|
||||
the_list = self.test_content
|
||||
|
|
@ -171,47 +205,38 @@ class NotificationOverlayWindow(OverlayWindow):
|
|||
get_surface(self.recv_icon, icon, icon,
|
||||
self.icon_size)
|
||||
|
||||
def recv_icon(self, identifier, pix, mask):
|
||||
"""
|
||||
Called when image_getter has downloaded an image
|
||||
"""
|
||||
def recv_icon(self, identifier, pix, _mask):
|
||||
"""Callback from image_getter for icons"""
|
||||
self.image_list[identifier] = pix
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_fg(self, fg_col):
|
||||
"""
|
||||
Set default text colour
|
||||
"""
|
||||
"""Config option: Set default text colour"""
|
||||
if self.fg_col != fg_col:
|
||||
self.fg_col = fg_col
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_bg(self, bg_col):
|
||||
"""
|
||||
Set background colour
|
||||
"""
|
||||
"""Config option: Set background colour"""
|
||||
if self.bg_col != bg_col:
|
||||
self.bg_col = bg_col
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_show_icon(self, icon):
|
||||
"""
|
||||
Set if icons should be shown inline
|
||||
"""
|
||||
"""Config option: Set if icons should be shown inline"""
|
||||
if self.show_icon != icon:
|
||||
self.show_icon = icon
|
||||
self.set_needs_redraw()
|
||||
self.get_all_images()
|
||||
|
||||
def set_reverse_order(self, rev):
|
||||
"""Config option: Reverse order of messages"""
|
||||
if self.reverse_order != rev:
|
||||
self.reverse_order = rev
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_font(self, font):
|
||||
"""
|
||||
Set font used to render text
|
||||
"""
|
||||
"""Config option: Font used to render text"""
|
||||
if self.text_font != font:
|
||||
self.text_font = font
|
||||
|
||||
|
|
@ -222,13 +247,13 @@ class NotificationOverlayWindow(OverlayWindow):
|
|||
self.set_needs_redraw()
|
||||
|
||||
def recv_attach(self, identifier, pix):
|
||||
"""
|
||||
Called when an image has been downloaded by image_getter
|
||||
"""
|
||||
"""Callback from image_getter for attachments"""
|
||||
self.icons[identifier] = pix
|
||||
self.set_needs_redraw()
|
||||
|
||||
def calc_all_height(self):
|
||||
"""Return the height in window-space pixels required
|
||||
to draw this overlay with current dataset"""
|
||||
h = 0
|
||||
my_list = self.content
|
||||
if self.testing:
|
||||
|
|
@ -240,6 +265,7 @@ class NotificationOverlayWindow(OverlayWindow):
|
|||
return h
|
||||
|
||||
def calc_height(self, line):
|
||||
"""Return height in window-space pixels required to draw individual notification"""
|
||||
icon_width = 0
|
||||
icon_pad = 0
|
||||
icon = line['icon']
|
||||
|
|
@ -257,9 +283,8 @@ class NotificationOverlayWindow(OverlayWindow):
|
|||
layout = self.create_pango_layout(message)
|
||||
layout.set_auto_dir(True)
|
||||
layout.set_markup(message, -1)
|
||||
attr = layout.get_attributes()
|
||||
(floating_x, floating_y, floating_width,
|
||||
floating_height) = self.get_floating_coords()
|
||||
(_floating_x, _floating_y, floating_width,
|
||||
_floating_height) = self.get_floating_coords()
|
||||
width = self.limit_width if floating_width > self.limit_width else floating_width
|
||||
layout.set_width((Pango.SCALE * (width -
|
||||
(self.border_radius * 4 + icon_width + icon_pad))))
|
||||
|
|
@ -267,12 +292,13 @@ class NotificationOverlayWindow(OverlayWindow):
|
|||
if self.text_font:
|
||||
font = Pango.FontDescription(self.text_font)
|
||||
layout.set_font_description(font)
|
||||
text_width, text_height = layout.get_pixel_size()
|
||||
_text_width, text_height = layout.get_pixel_size()
|
||||
if text_height < icon_width:
|
||||
text_height = icon_width
|
||||
return text_height + (self.border_radius*4) + self.padding
|
||||
|
||||
def has_content(self):
|
||||
"""Return true if this overlay has meaningful content to show"""
|
||||
if not self.enabled:
|
||||
return False
|
||||
if self.hidden:
|
||||
|
|
@ -282,15 +308,13 @@ class NotificationOverlayWindow(OverlayWindow):
|
|||
return self.content
|
||||
|
||||
def overlay_draw(self, w, context, data=None):
|
||||
"""
|
||||
Draw the overlay
|
||||
"""
|
||||
"""Draw the overlay"""
|
||||
if self.piggyback:
|
||||
self.piggyback.overlay_draw(w, context, data)
|
||||
if not self.enabled:
|
||||
return
|
||||
self.context = context
|
||||
(width, height) = self.get_size()
|
||||
(_width, height) = self.get_size()
|
||||
if not self.piggyback_parent:
|
||||
context.set_antialias(cairo.ANTIALIAS_GOOD)
|
||||
|
||||
|
|
@ -319,7 +343,6 @@ class NotificationOverlayWindow(OverlayWindow):
|
|||
current_y = 0
|
||||
if self.align_vert == 1: # Center. Oh god why
|
||||
current_y = (height/2.0) - (self.calc_all_height() / 2.0)
|
||||
tnow = time.time()
|
||||
if self.testing:
|
||||
the_list = self.test_content
|
||||
else:
|
||||
|
|
@ -354,10 +377,7 @@ class NotificationOverlayWindow(OverlayWindow):
|
|||
self.context = None
|
||||
|
||||
def draw_text(self, pos_y, text, icon):
|
||||
"""
|
||||
Draw a text message, returning the Y position of the next message
|
||||
"""
|
||||
|
||||
"""Draw a text message, returning the Y position of the next message"""
|
||||
icon_width = self.icon_size
|
||||
icon_pad = self.icon_pad
|
||||
if not self.show_icon:
|
||||
|
|
@ -371,8 +391,8 @@ class NotificationOverlayWindow(OverlayWindow):
|
|||
layout.set_markup(text, -1)
|
||||
attr = layout.get_attributes()
|
||||
|
||||
(floating_x, floating_y, floating_width,
|
||||
floating_height) = self.get_floating_coords()
|
||||
(_floating_x, _floating_y, floating_width,
|
||||
_floating_height) = self.get_floating_coords()
|
||||
width = self.limit_width if floating_width > self.limit_width else floating_width
|
||||
layout.set_width((Pango.SCALE * (width -
|
||||
(self.border_radius * 4 + icon_width + icon_pad))))
|
||||
|
|
@ -475,7 +495,6 @@ class NotificationOverlayWindow(OverlayWindow):
|
|||
self.get_pango_context(), self.render_custom, None)
|
||||
|
||||
text = layout.get_text()
|
||||
count = 0
|
||||
|
||||
layout.set_attributes(attr)
|
||||
|
||||
|
|
@ -486,7 +505,6 @@ class NotificationOverlayWindow(OverlayWindow):
|
|||
self.get_pango_context(), self.render_custom, None)
|
||||
|
||||
text = layout.get_text()
|
||||
count = 0
|
||||
|
||||
layout.set_attributes(attr)
|
||||
|
||||
|
|
@ -501,11 +519,9 @@ class NotificationOverlayWindow(OverlayWindow):
|
|||
return next_y
|
||||
|
||||
def render_custom(self, ctx, shape, path, _data):
|
||||
"""
|
||||
Draw an inline image as a custom emoticon
|
||||
"""
|
||||
"""Draw an inline image as a custom emoticon"""
|
||||
if shape.data >= len(self.image_list):
|
||||
log.warning(f"{shape.data} >= {len(self.image_list)}")
|
||||
log.warning("%s >= %s", shape.data, len(self.image_list))
|
||||
return
|
||||
# key is the url to the image
|
||||
key = self.image_list[shape.data]
|
||||
|
|
@ -521,9 +537,7 @@ class NotificationOverlayWindow(OverlayWindow):
|
|||
return True
|
||||
|
||||
def sanitize_string(self, string):
|
||||
"""
|
||||
Sanitize a text message so that it doesn't intefere with Pango's XML format
|
||||
"""
|
||||
"""Sanitize a text message so that it doesn't intefere with Pango's XML format"""
|
||||
string = string.replace("&", "&")
|
||||
string = string.replace("<", "<")
|
||||
string = string .replace(">", ">")
|
||||
|
|
@ -532,6 +546,7 @@ class NotificationOverlayWindow(OverlayWindow):
|
|||
return string
|
||||
|
||||
def set_testing(self, testing):
|
||||
"""Toggle placeholder images for testing"""
|
||||
self.testing = testing
|
||||
self.set_needs_redraw()
|
||||
self.get_all_images()
|
||||
|
|
|
|||
|
|
@ -15,11 +15,9 @@ Overlay parent class. Helpful if we need more overlay
|
|||
types without copy-and-pasting too much code
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import gi
|
||||
import cairo
|
||||
import Xlib
|
||||
from Xlib.display import Display
|
||||
from Xlib import X, Xatom
|
||||
gi.require_version("Gtk", "3.0")
|
||||
|
|
@ -46,7 +44,7 @@ class OverlayWindow(Gtk.Window):
|
|||
"""
|
||||
window = Gtk.Window()
|
||||
screen = window.get_screen()
|
||||
screen_type = "%s" % (screen)
|
||||
screen_type = f"{screen}"
|
||||
self.is_wayland = False
|
||||
if "Wayland" in screen_type:
|
||||
self.is_wayland = True
|
||||
|
|
@ -76,7 +74,7 @@ class OverlayWindow(Gtk.Window):
|
|||
if not self.get_display().supports_input_shapes():
|
||||
log.info(
|
||||
"Input shapes not available. Quitting")
|
||||
sys.exit(1)
|
||||
self.discover.exit()
|
||||
if visual:
|
||||
# Set the visual even if we can't use it right now
|
||||
self.set_visual(visual)
|
||||
|
|
@ -124,10 +122,12 @@ class OverlayWindow(Gtk.Window):
|
|||
# this process hanging if it happens
|
||||
self.connect('destroy', self.window_exited)
|
||||
|
||||
def window_exited(self, window=None):
|
||||
sys.exit(1)
|
||||
def window_exited(self, _window=None):
|
||||
"""Window closed. Exit app"""
|
||||
self.discover.exit()
|
||||
|
||||
def set_gamescope_xatom(self, enabled):
|
||||
"""Set Gamescope XAtom to identify self as an overlay candidate"""
|
||||
if self.piggyback_parent:
|
||||
return
|
||||
|
||||
|
|
@ -136,7 +136,7 @@ class OverlayWindow(Gtk.Window):
|
|||
self.is_xatom_set = enabled
|
||||
display = Display()
|
||||
atom = display.intern_atom("GAMESCOPE_EXTERNAL_OVERLAY")
|
||||
opaq = display.intern_atom("_NET_WM_WINDOW_OPACITY")
|
||||
# Since unused: _NET_WM_WINDOW_OPACITY
|
||||
|
||||
if self.get_toplevel().get_window():
|
||||
topw = display.create_resource_object(
|
||||
|
|
@ -148,7 +148,7 @@ class OverlayWindow(Gtk.Window):
|
|||
log.info("Setting GAMESCOPE_EXTERNAL_OVERLAY to %s", enabled)
|
||||
display.sync()
|
||||
else:
|
||||
log.warn("Unable to set GAMESCOPE_EXTERNAL_OVERLAY")
|
||||
log.warning("Unable to set GAMESCOPE_EXTERNAL_OVERLAY")
|
||||
|
||||
def set_wayland_state(self):
|
||||
"""
|
||||
|
|
@ -159,7 +159,7 @@ class OverlayWindow(Gtk.Window):
|
|||
log.info(
|
||||
"GTK Layer Shell is not supported on this wayland compositor")
|
||||
log.info("Currently not possible: Gnome, Weston")
|
||||
sys.exit(0)
|
||||
self.discover.exit()
|
||||
if not GtkLayerShell.is_layer_window(self):
|
||||
GtkLayerShell.init_for_window(self)
|
||||
GtkLayerShell.set_layer(self, GtkLayerShell.Layer.OVERLAY)
|
||||
|
|
@ -169,13 +169,16 @@ class OverlayWindow(Gtk.Window):
|
|||
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.TOP, True)
|
||||
|
||||
def set_piggyback(self, other_overlay):
|
||||
"""Sets as piggybacking off the given (other) overlay"""
|
||||
other_overlay.piggyback = self
|
||||
self.piggyback_parent = other_overlay
|
||||
|
||||
def has_content(self):
|
||||
"""Return true if overlay has meaningful content"""
|
||||
return False
|
||||
|
||||
def overlay_draw_pre(self, _w, context, data=None):
|
||||
"""Prepare for drawing the overlay. Calls overlay_draw after preparations"""
|
||||
content = self.has_content()
|
||||
if self.piggyback and self.piggyback.has_content():
|
||||
content = True
|
||||
|
|
@ -224,16 +227,18 @@ class OverlayWindow(Gtk.Window):
|
|||
"""
|
||||
if width > 1.0 and height > 1.0:
|
||||
# Old data.
|
||||
(screen_x, screen_y, screen_width,
|
||||
(_screen_x, _screen_y, screen_width,
|
||||
screen_height) = self.get_display_coords()
|
||||
pos_x = float(pos_x) / screen_width
|
||||
pos_y = float(pos_y) / screen_height
|
||||
width = float(width) / screen_width
|
||||
height = float(height) / screen_height
|
||||
|
||||
if self.floating != floating or self.pos_x != pos_x or self.pos_y != pos_y or self.width != width or self.height != height:
|
||||
if (self.floating != floating or self.pos_x != pos_x or
|
||||
self.pos_y != pos_y or self.width != width or self.height != height):
|
||||
# Special case for Cinnamon desktop : see https://github.com/trigg/Discover/issues/322
|
||||
if 'XDG_SESSION_DESKTOP' in os.environ and os.environ['XDG_SESSION_DESKTOP'] == 'cinnamon':
|
||||
if ('XDG_SESSION_DESKTOP' in os.environ and
|
||||
os.environ['XDG_SESSION_DESKTOP'] == 'cinnamon'):
|
||||
floating = True
|
||||
|
||||
self.floating = floating
|
||||
|
|
@ -258,6 +263,7 @@ class OverlayWindow(Gtk.Window):
|
|||
self.input_shape_combine_region(reg)
|
||||
|
||||
def set_hide_on_mouseover(self, hide):
|
||||
"""Set if the overlay should hide when mouse moves over it"""
|
||||
if self.hide_on_mouseover != hide:
|
||||
self.hide_on_mouseover = hide
|
||||
if self.hide_on_mouseover:
|
||||
|
|
@ -266,6 +272,7 @@ class OverlayWindow(Gtk.Window):
|
|||
self.set_untouchable()
|
||||
|
||||
def set_mouseover_timer(self, time):
|
||||
"""Set the time until the overlay reappears after mouse over"""
|
||||
self.timeout_mouse_over = time
|
||||
|
||||
def unset_shape(self):
|
||||
|
|
@ -299,6 +306,7 @@ class OverlayWindow(Gtk.Window):
|
|||
self.set_needs_redraw()
|
||||
|
||||
def get_display_coords(self):
|
||||
"""Get screen space co-ordinates of the monitor"""
|
||||
if self.piggyback_parent:
|
||||
return self.piggyback_parent.get_display_coords()
|
||||
monitor = self.get_monitor_from_plug()
|
||||
|
|
@ -311,29 +319,35 @@ class OverlayWindow(Gtk.Window):
|
|||
return (0, 0, 1920, 1080)
|
||||
|
||||
def get_floating_coords(self):
|
||||
"""Get screen space co-ordinates of the window"""
|
||||
(screen_x, screen_y, screen_width, screen_height) = self.get_display_coords()
|
||||
if self.floating:
|
||||
if self.pos_x == None or self.pos_y == None or self.width == None or self.height == None:
|
||||
if (self.pos_x is None or self.pos_y is None or
|
||||
self.width is None or self.height is None):
|
||||
log.error("No usable floating position")
|
||||
|
||||
if not self.is_wayland:
|
||||
return (screen_x + self.pos_x * screen_width, screen_y + self.pos_y * screen_height, self.width * screen_width, self.height * screen_height)
|
||||
return (self.pos_x * screen_width, self.pos_y * screen_height, self.width * screen_width, self.height * screen_height)
|
||||
return (screen_x + self.pos_x * screen_width, screen_y + self.pos_y * screen_height,
|
||||
self.width * screen_width, self.height * screen_height)
|
||||
return (self.pos_x * screen_width, self.pos_y * screen_height,
|
||||
self.width * screen_width, self.height * screen_height)
|
||||
else:
|
||||
return (screen_x, screen_y, screen_width, screen_height)
|
||||
|
||||
def set_needs_redraw(self, be_pushy=False):
|
||||
"""Schedule this overlay for a redraw. If part of a
|
||||
piggyback chain, pass it up to be redrawn by topmost parent"""
|
||||
if (not self.hidden and self.enabled) or be_pushy:
|
||||
if self.piggyback_parent:
|
||||
self.piggyback_parent.set_needs_redraw(be_pushy=True)
|
||||
|
||||
if self.redraw_id == None:
|
||||
if self.redraw_id is None:
|
||||
self.redraw_id = GLib.idle_add(self.redraw)
|
||||
else:
|
||||
log.debug("Already awaiting paint")
|
||||
|
||||
# If this overlay has data that expires after draw, plan for that here
|
||||
if self.timer_after_draw != None:
|
||||
if self.timer_after_draw is not None:
|
||||
GLib.timeout_add_seconds(self.timer_after_draw, self.redraw)
|
||||
|
||||
def redraw(self):
|
||||
|
|
@ -364,6 +378,7 @@ class OverlayWindow(Gtk.Window):
|
|||
return False
|
||||
|
||||
def set_hidden(self, hidden):
|
||||
"""Set if the overlay should be hidden"""
|
||||
self.hidden = hidden
|
||||
self.set_enabled(self.enabled)
|
||||
|
||||
|
|
@ -371,7 +386,7 @@ class OverlayWindow(Gtk.Window):
|
|||
"""
|
||||
Set the monitor this overlay should display on.
|
||||
"""
|
||||
plug_name = "%s" % (idx)
|
||||
plug_name = f"{idx}"
|
||||
if self.monitor != plug_name:
|
||||
self.monitor = plug_name
|
||||
if self.is_wayland:
|
||||
|
|
@ -387,6 +402,8 @@ class OverlayWindow(Gtk.Window):
|
|||
self.set_needs_redraw()
|
||||
|
||||
def get_monitor_from_plug(self):
|
||||
"""Return a GDK Monitor filtered by plug name
|
||||
(HDMI-1, eDP-1, VGA etc)"""
|
||||
if not self.monitor or self.monitor == "Any":
|
||||
return None
|
||||
display = Gdk.Display.get_default()
|
||||
|
|
@ -454,26 +471,31 @@ class OverlayWindow(Gtk.Window):
|
|||
self.hide()
|
||||
|
||||
def set_task(self, visible):
|
||||
"""Set visible on taskbar. Not working at last check"""
|
||||
self.set_skip_pager_hint(not visible)
|
||||
self.set_skip_taskbar_hint(not visible)
|
||||
|
||||
def check_composite(self, _a=None, _b=None):
|
||||
# Called when an X11 session switched compositing on or off
|
||||
"""Callback for compositing started/stopped in X11"""
|
||||
self.redraw()
|
||||
|
||||
def screen_changed(self, screen=None):
|
||||
def screen_changed(self, _screen=None):
|
||||
"""Callback to set monitor to display on"""
|
||||
self.set_monitor(self.monitor)
|
||||
|
||||
def mouseover(self, a=None, b=None):
|
||||
def mouseover(self, _a=None, _b=None):
|
||||
"""Callback when mouseover occurs, hides overlay"""
|
||||
self.draw_blank = True
|
||||
self.set_needs_redraw()
|
||||
return True
|
||||
|
||||
def mouseout(self, a=None, b=None):
|
||||
def mouseout(self, _a=None, _b=None):
|
||||
"""Callback when mouseout occurs, sets a timer to show overlay"""
|
||||
GLib.timeout_add_seconds(self.timeout_mouse_over, self.mouseout_timed)
|
||||
return True
|
||||
|
||||
def mouseout_timed(self, a=None, b=None):
|
||||
def mouseout_timed(self, _a=None, _b=None):
|
||||
"""Callback a short while after mouseout occured, shows overlay"""
|
||||
self.draw_blank = False
|
||||
self.set_needs_redraw()
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -11,18 +11,19 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""Settings window holding all settings tab"""
|
||||
# pylint: disable=missing-function-docstring
|
||||
import gettext
|
||||
import gi
|
||||
import logging
|
||||
import pkg_resources
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
from configparser import ConfigParser
|
||||
import gi
|
||||
import pkg_resources
|
||||
from .autostart import Autostart, BazziteAutostart
|
||||
from .draggable_window import DraggableWindow
|
||||
from .draggable_window_wayland import DraggableWindowWayland
|
||||
|
||||
from configparser import ConfigParser
|
||||
gi.require_version("Gtk", "3.0")
|
||||
# pylint: disable=wrong-import-position,wrong-import-order
|
||||
from gi.repository import Gtk, Gdk, Gio # nopep8
|
||||
|
|
@ -75,6 +76,14 @@ class MainSettingsWindow():
|
|||
self.current_guild = "0"
|
||||
self.current_channel = "0"
|
||||
self.hidden_overlay = False
|
||||
self.voice_floating_x = 0
|
||||
self.voice_floating_y = 0
|
||||
self.voice_floating_w = 0
|
||||
self.voice_floating_h = 0
|
||||
self.text_floating_x = 0
|
||||
self.text_floating_y = 0
|
||||
self.text_floating_w = 0
|
||||
self.text_floating_h = 0
|
||||
|
||||
self.menu = self.make_menu()
|
||||
self.make_sys_tray_icon(self.menu)
|
||||
|
|
@ -111,16 +120,30 @@ class MainSettingsWindow():
|
|||
if name.endswith("_all"):
|
||||
widget.set_label(_(widget.get_label()))
|
||||
|
||||
self.widget['overview_main_text'].set_markup("<span size=\"larger\">%s (%s)</span>\n\n%s\n\n%s (<a href=\"https://discord.gg/jRKWMuDy5V\">https://discord.gg/jRKWMuDy5V</a>) %s (<a href=\"https://github.com/trigg/Discover\">https://github.com/trigg/Discover</a>)\n\n\n\n\n\n" % (
|
||||
_("Welcome to Discover Overlay"),
|
||||
pkg_resources.get_distribution('discover_overlay').version,
|
||||
_("Discover-Overlay is a GTK3 overlay written in Python3. It can be configured to show who is currently talking on discord or it can be set to display text and images from a preconfigured channel. It is fully customisable and can be configured to display anywhere on the screen. We fully support X11 and wlroots based environments. We felt the need to make this project due to the shortcomings in support on Linux by the official discord client."),
|
||||
_("Please visit our discord"),
|
||||
_(" for support. Or open an issue on our GitHub ")
|
||||
))
|
||||
self.widget['overview_main_text'].set_markup(
|
||||
"%s%s (%s)%s%s\n\n%s %s %s %s%s\n\n\n\n\n\n" % (
|
||||
"<span size=\"larger\">",
|
||||
_("Welcome to Discover Overlay"),
|
||||
pkg_resources.get_distribution('discover_overlay').version,
|
||||
"</span>\n\n",
|
||||
_(("Discover-Overlay is a GTK3 overlay written in Python3."
|
||||
" It can be configured to show who is currently talking"
|
||||
" on discord or it can be set to display text and images"
|
||||
" from a preconfigured channel. It is fully customisable"
|
||||
" and can be configured to display anywhere on the screen."
|
||||
" We fully support X11 and wlroots based environments. We "
|
||||
"felt the need to make this project due to the shortcomings"
|
||||
" in support on Linux by the official discord client.")),
|
||||
_("Please visit our discord"),
|
||||
"(<a href=\"https://discord.gg/jRKWMuDy5V\">https://discord.gg/jRKWMuDy5V</a>)",
|
||||
_(" for support. Or open an issue on our GitHub "),
|
||||
"(<a href=\"https://github.com/trigg/Discover\">",
|
||||
"https://github.com/trigg/Discover</a>)"
|
||||
)
|
||||
)
|
||||
|
||||
screen = window.get_screen()
|
||||
screen_type = "%s" % (screen)
|
||||
screen_type = f"{screen}"
|
||||
self.is_wayland = False
|
||||
if "Wayland" in screen_type:
|
||||
self.is_wayland = True
|
||||
|
|
@ -185,6 +208,7 @@ class MainSettingsWindow():
|
|||
self.widget['window'].set_default_icon_name(self.icon_name)
|
||||
|
||||
def set_steamos_window_size(self):
|
||||
"""Set window based on steamos usage"""
|
||||
# Huge bunch of assumptions.
|
||||
# Gamescope only has one monitor
|
||||
# Gamescope has no scale factor
|
||||
|
|
@ -193,23 +217,21 @@ class MainSettingsWindow():
|
|||
monitor = display.get_monitor(0)
|
||||
if monitor:
|
||||
geometry = monitor.get_geometry()
|
||||
scale_factor = monitor.get_scale_factor()
|
||||
log.info("%d %d" % (geometry.width, geometry.height))
|
||||
log.info("%d %d", geometry.width, geometry.height)
|
||||
self.window.set_size_request(geometry.width, geometry.height)
|
||||
|
||||
def keypress_in_settings(self, window, event):
|
||||
"""Callback to steal keypresses to assist SteamOS gamepad control"""
|
||||
if self.spinning_focus:
|
||||
match event.keyval:
|
||||
case Gdk.KEY_Right:
|
||||
step = self.spinning_focus.get_increments().step
|
||||
value = self.spinning_focus.get_value()
|
||||
self.spinning_focus.set_value(value + step)
|
||||
pass
|
||||
case Gdk.KEY_Left:
|
||||
step = self.spinning_focus.get_increments().step
|
||||
value = self.spinning_focus.get_value()
|
||||
self.spinning_focus.set_value(value - step)
|
||||
pass
|
||||
case Gdk.KEY_Up:
|
||||
step = self.spinning_focus.get_increments().step
|
||||
value = self.spinning_focus.get_value()
|
||||
|
|
@ -230,11 +252,9 @@ class MainSettingsWindow():
|
|||
case Gdk.KEY_Right:
|
||||
value = self.scale_focus.get_value()
|
||||
self.scale_focus.set_value(value + 0.1)
|
||||
pass
|
||||
case Gdk.KEY_Left:
|
||||
value = self.scale_focus.get_value()
|
||||
self.scale_focus.set_value(value - 0.1)
|
||||
pass
|
||||
case Gdk.KEY_Up:
|
||||
value = self.scale_focus.get_value()
|
||||
self.scale_focus.set_value(value + 0.1)
|
||||
|
|
@ -267,7 +287,7 @@ class MainSettingsWindow():
|
|||
widget = self.window.get_focus()
|
||||
if widget:
|
||||
# I really want there to be a better way...
|
||||
widget_type = "%s" % (widget)
|
||||
widget_type = f"{widget}"
|
||||
if 'Gtk.SpinButton' in widget_type:
|
||||
self.spinning_focus = widget
|
||||
|
||||
|
|
@ -285,16 +305,19 @@ class MainSettingsWindow():
|
|||
return True
|
||||
|
||||
def request_channels_from_guild(self, guild_id):
|
||||
with open(self.rpc_file, 'w') as f:
|
||||
f.write('--rpc --guild-request=%s' % (guild_id))
|
||||
"""Send RPC to overlay to request updated channel list"""
|
||||
with open(self.rpc_file, 'w', encoding="utf-8") as f:
|
||||
f.write(f"--rpc --guild-request={guild_id}")
|
||||
|
||||
def populate_guild_menu(self, _a=None, _b=None, _c=None, _d=None):
|
||||
"""Read guild data and repopulate widget.
|
||||
Disable signal handling meanwhile to avoid recursive logic"""
|
||||
g = self.widget['text_server']
|
||||
c = self.widget['text_channel']
|
||||
g.handler_block(self.server_handler)
|
||||
c.handler_block(self.channel_handler)
|
||||
try:
|
||||
with open(self.channel_file, "r") as tfile:
|
||||
with open(self.channel_file, "r", encoding="utf-8") as tfile:
|
||||
data = tfile.readlines()
|
||||
if len(data) >= 1:
|
||||
data = json.loads(data[0])
|
||||
|
|
@ -322,21 +345,22 @@ class MainSettingsWindow():
|
|||
c.handler_unblock(self.channel_handler)
|
||||
|
||||
def populate_monitor_menus(self, _a=None, _b=None):
|
||||
v = self.widget['voice_monitor']
|
||||
t = self.widget['text_monitor']
|
||||
m = self.widget['notification_monitor']
|
||||
"""Get Monitor list from GTK and repopulate widget"""
|
||||
voice = self.widget['voice_monitor']
|
||||
text = self.widget['text_monitor']
|
||||
notify = self.widget['notification_monitor']
|
||||
|
||||
v_value = v.get_active()
|
||||
t_value = t.get_active()
|
||||
m_value = m.get_active()
|
||||
v_value = voice.get_active()
|
||||
t_value = text.get_active()
|
||||
m_value = notify.get_active()
|
||||
|
||||
v.remove_all()
|
||||
t.remove_all()
|
||||
m.remove_all()
|
||||
voice.remove_all()
|
||||
text.remove_all()
|
||||
notify.remove_all()
|
||||
|
||||
v.append_text("Any")
|
||||
t.append_text("Any")
|
||||
m.append_text("Any")
|
||||
voice.append_text("Any")
|
||||
text.append_text("Any")
|
||||
notify.append_text("Any")
|
||||
|
||||
display = Gdk.Display.get_default()
|
||||
screen = self.window.get_screen()
|
||||
|
|
@ -348,41 +372,38 @@ class MainSettingsWindow():
|
|||
manufacturer = this_mon.get_manufacturer()
|
||||
model = this_mon.get_model()
|
||||
connector = screen.get_monitor_plug_name(i)
|
||||
monitor_label = "%s %s\n%s" % (
|
||||
manufacturer, model, connector)
|
||||
v.append_text(monitor_label)
|
||||
t.append_text(monitor_label)
|
||||
m.append_text(monitor_label)
|
||||
monitor_label = f"{manufacturer} {model}\n{connector}"
|
||||
voice.append_text(monitor_label)
|
||||
text.append_text(monitor_label)
|
||||
notify.append_text(monitor_label)
|
||||
|
||||
v.set_active(v_value)
|
||||
t.set_active(t_value)
|
||||
m.set_active(m_value)
|
||||
voice.set_active(v_value)
|
||||
text.set_active(t_value)
|
||||
notify.set_active(m_value)
|
||||
|
||||
def close_window(self, widget=None, event=None):
|
||||
"""
|
||||
Hide the settings window for use at a later date
|
||||
"""
|
||||
def close_window(self, _widget=None, _event=None):
|
||||
"""Hide the settings window for use at a later date"""
|
||||
self.window.hide()
|
||||
if self.ind == None and self.tray == None:
|
||||
if self.ind is None and self.tray is None:
|
||||
sys.exit(0)
|
||||
if self.ind != None:
|
||||
if self.ind is not None:
|
||||
# pylint: disable=import-outside-toplevel
|
||||
from gi.repository import AppIndicator3
|
||||
if self.ind.get_status() == AppIndicator3.IndicatorStatus.PASSIVE:
|
||||
sys.exit(0)
|
||||
return True
|
||||
|
||||
def close_app(self, widget=None, event=None):
|
||||
def close_app(self, _widget=None, _event=None):
|
||||
"""Close the app"""
|
||||
sys.exit(0)
|
||||
|
||||
def present_settings(self, _a=None):
|
||||
"""
|
||||
Show the settings window
|
||||
"""
|
||||
"""Show the settings window"""
|
||||
self.widget['notebook'].set_current_page(0)
|
||||
self.window.show()
|
||||
|
||||
def set_alignment_labels(self, horz):
|
||||
"""Relabel alignment pulldowns"""
|
||||
m1 = self.widget['voice_align_1'].get_model()
|
||||
m2 = self.widget['voice_align_2'].get_model()
|
||||
i = m1.get_iter_first()
|
||||
|
|
@ -409,6 +430,7 @@ class MainSettingsWindow():
|
|||
m2.set_value(i2, 0, _("Bottom"))
|
||||
|
||||
def read_config(self):
|
||||
"""Read config from disk"""
|
||||
self.loading_config = True
|
||||
|
||||
# Read config and put into gui
|
||||
|
|
@ -522,8 +544,8 @@ class MainSettingsWindow():
|
|||
self.widget['voice_square_avatar'].set_active(config.getboolean(
|
||||
"main", "square_avatar", fallback=True))
|
||||
|
||||
self.widget['voice_fancy_avatar_shapes'].set_active(config.getboolean("main",
|
||||
"fancy_border", fallback=True))
|
||||
self.widget['voice_fancy_avatar_shapes'].set_active(
|
||||
config.getboolean("main", "fancy_border", fallback=True))
|
||||
|
||||
self.widget['voice_order_avatars_by'].set_active(
|
||||
config.getint("main", "order", fallback=0))
|
||||
|
|
@ -719,6 +741,7 @@ class MainSettingsWindow():
|
|||
self.loading_config = False
|
||||
|
||||
def make_colour(self, col):
|
||||
"""Create a Gdk Color from a col tuple"""
|
||||
col = json.loads(col)
|
||||
return Gdk.RGBA(col[0], col[1], col[2], col[3])
|
||||
|
||||
|
|
@ -732,6 +755,7 @@ class MainSettingsWindow():
|
|||
return guild_ids
|
||||
|
||||
def get_monitor_index_from_plug(self, monitor):
|
||||
"""Get monitor index from plug name"""
|
||||
if not monitor or monitor == "Any":
|
||||
return 0
|
||||
display = Gdk.Display.get_default()
|
||||
|
|
@ -746,9 +770,7 @@ class MainSettingsWindow():
|
|||
return 0
|
||||
|
||||
def get_monitor_obj(self, idx):
|
||||
"""
|
||||
Helper function to find the monitor object of the monitor
|
||||
"""
|
||||
"""Helper function to find the monitor object of the monitor"""
|
||||
display = Gdk.Display.get_default()
|
||||
return display.get_monitor(idx)
|
||||
|
||||
|
|
@ -780,17 +802,13 @@ class MainSettingsWindow():
|
|||
self.tray.set_visible(False)
|
||||
|
||||
def show_menu(self, obj, button, time):
|
||||
"""
|
||||
Show menu when System Tray icon is clicked
|
||||
"""
|
||||
"""Show menu when System Tray icon is clicked"""
|
||||
self.menu.show_all()
|
||||
self.menu.popup(
|
||||
None, None, Gtk.StatusIcon.position_menu, obj, button, time)
|
||||
|
||||
def set_sys_tray_icon_visible(self, visible):
|
||||
"""
|
||||
Sets whether the tray icon is visible
|
||||
"""
|
||||
"""Sets whether the tray icon is visible"""
|
||||
if self.ind is not None:
|
||||
# pylint: disable=import-outside-toplevel
|
||||
from gi.repository import AppIndicator3
|
||||
|
|
@ -800,9 +818,7 @@ class MainSettingsWindow():
|
|||
self.tray.set_visible(visible)
|
||||
|
||||
def make_menu(self):
|
||||
"""
|
||||
Create System Menu
|
||||
"""
|
||||
"""Create System Menu"""
|
||||
menu = Gtk.Menu()
|
||||
settings_opt = Gtk.MenuItem.new_with_label(_("Settings"))
|
||||
self.toggle_opt = Gtk.MenuItem.new_with_label(_("Hide overlay"))
|
||||
|
|
@ -822,11 +838,13 @@ class MainSettingsWindow():
|
|||
return menu
|
||||
|
||||
def toggle_overlay(self, _a=None, _b=None):
|
||||
"""Toggle overlay visibility"""
|
||||
self.hidden_overlay = not self.hidden_overlay
|
||||
self.config_set("general", "hideoverlay", "%s" % (self.hidden_overlay))
|
||||
self.config_set("general", "hideoverlay", f"{self.hidden_overlay}")
|
||||
self.update_toggle_overlay()
|
||||
|
||||
def update_toggle_overlay(self, _a=None, _b=None):
|
||||
"""Update gui to reflect state of overlay visibility"""
|
||||
self.widget['core_hide_overlay'].handler_block(
|
||||
self.hidden_overlay_handler)
|
||||
|
||||
|
|
@ -840,14 +858,17 @@ class MainSettingsWindow():
|
|||
self.toggle_opt.set_label(_("Hide overlay"))
|
||||
|
||||
def close_overlay(self, _a=None, _b=None):
|
||||
with open(self.rpc_file, 'w') as f:
|
||||
"""Send RPC to tell the overlay to close"""
|
||||
with open(self.rpc_file, 'w', encoding="utf-8") as f:
|
||||
f.write('--rpc --close')
|
||||
|
||||
def overview_close(self, button):
|
||||
def overview_close(self, _button):
|
||||
"""Gui callback to close overlay. Remove and use close_overlay?"""
|
||||
log.info("Quit pressed")
|
||||
self.close_overlay()
|
||||
|
||||
def voice_place_window(self, button):
|
||||
"""Toggle the voice placement"""
|
||||
if self.voice_placement_window:
|
||||
(pos_x, pos_y, width, height) = self.voice_placement_window.get_coords()
|
||||
self.voice_floating_x = pos_x
|
||||
|
|
@ -857,18 +878,14 @@ class MainSettingsWindow():
|
|||
|
||||
config = ConfigParser(interpolation=None)
|
||||
config.read(self.config_file)
|
||||
if not "main" in config.sections():
|
||||
if "main" not in config.sections():
|
||||
config.add_section("main")
|
||||
config.set("main", "floating_x", "%f" %
|
||||
(self.voice_floating_x))
|
||||
config.set("main", "floating_y", "%f" %
|
||||
(self.voice_floating_y))
|
||||
config.set("main", "floating_w", "%f" %
|
||||
(self.voice_floating_w))
|
||||
config.set("main", "floating_h", "%f" %
|
||||
(self.voice_floating_h))
|
||||
config.set("main", "floating_x", f"{self.voice_floating_x:f}")
|
||||
config.set("main", "floating_y", f"{self.voice_floating_y:f}")
|
||||
config.set("main", "floating_w", f"{self.voice_floating_w:f}")
|
||||
config.set("main", "floating_h", f"{self.voice_floating_h:f}")
|
||||
|
||||
with open(self.config_file, 'w') as file:
|
||||
with open(self.config_file, 'w', encoding="utf-8") as file:
|
||||
config.write(file)
|
||||
if button:
|
||||
button.set_label(_("Place Window"))
|
||||
|
|
@ -890,11 +907,13 @@ class MainSettingsWindow():
|
|||
self.voice_placement_window = DraggableWindow(
|
||||
pos_x=self.voice_floating_x, pos_y=self.voice_floating_y,
|
||||
width=self.voice_floating_w, height=self.voice_floating_h,
|
||||
message=_("Place & resize this window then press Save!"), settings=self, monitor=self.widget['voice_monitor'].get_active()-1)
|
||||
message=_("Place & resize this window then press Save!"),
|
||||
settings=self, monitor=self.widget['voice_monitor'].get_active()-1)
|
||||
if button:
|
||||
button.set_label(_("Save this position"))
|
||||
|
||||
def text_place_window(self, button):
|
||||
"""Toggle the text placement"""
|
||||
if self.text_placement_window:
|
||||
(pos_x, pos_y, width, height) = self.text_placement_window.get_coords()
|
||||
self.text_floating_x = pos_x
|
||||
|
|
@ -904,18 +923,14 @@ class MainSettingsWindow():
|
|||
|
||||
config = ConfigParser(interpolation=None)
|
||||
config.read(self.config_file)
|
||||
if not "text" in config.sections():
|
||||
if "text" not in config.sections():
|
||||
config.add_section("text")
|
||||
config.set("text", "floating_x", "%f" %
|
||||
(self.text_floating_x))
|
||||
config.set("text", "floating_y", "%f" %
|
||||
(self.text_floating_y))
|
||||
config.set("text", "floating_w", "%f" %
|
||||
(self.text_floating_w))
|
||||
config.set("text", "floating_h", "%f" %
|
||||
(self.text_floating_h))
|
||||
config.set("text", "floating_x", f"{self.text_floating_x:f}")
|
||||
config.set("text", "floating_y", f"{self.text_floating_y:f}")
|
||||
config.set("text", "floating_w", f"{self.text_floating_w:f}")
|
||||
config.set("text", "floating_h", f"{self.text_floating_h:f}")
|
||||
|
||||
with open(self.config_file, 'w') as file:
|
||||
with open(self.config_file, 'w', encoding="utf-8") as file:
|
||||
config.write(file)
|
||||
if button:
|
||||
button.set_label(_("Place Window"))
|
||||
|
|
@ -937,21 +952,25 @@ class MainSettingsWindow():
|
|||
self.text_placement_window = DraggableWindow(
|
||||
pos_x=self.text_floating_x, pos_y=self.text_floating_y,
|
||||
width=self.text_floating_w, height=self.text_floating_h,
|
||||
message=_("Place & resize this window then press Save!"), settings=self, monitor=self.widget['text_monitor'].get_active()-1)
|
||||
message=_("Place & resize this window then press Save!"),
|
||||
settings=self, monitor=self.widget['text_monitor'].get_active()-1)
|
||||
if button:
|
||||
button.set_label(_("Save this position"))
|
||||
|
||||
def change_placement(self, placement_window):
|
||||
"""Finish window placement"""
|
||||
if placement_window == self.text_placement_window:
|
||||
self.text_place_window(None)
|
||||
elif placement_window == self.voice_placement_window:
|
||||
self.voice_place_window(None)
|
||||
|
||||
def text_server_refresh(self, button):
|
||||
with open(self.rpc_file, 'w') as f:
|
||||
def text_server_refresh(self, _button):
|
||||
"""Send RPC to overlay to request a list of text channels"""
|
||||
with open(self.rpc_file, 'w', encoding="utf-8") as f:
|
||||
f.write('--rpc --refresh-guilds')
|
||||
|
||||
def config_set(self, context, key, value):
|
||||
"""Write one key to config and save to disk"""
|
||||
if self.loading_config:
|
||||
return
|
||||
config = ConfigParser(interpolation=None)
|
||||
|
|
@ -959,10 +978,11 @@ class MainSettingsWindow():
|
|||
if not context in config.sections():
|
||||
config.add_section(context)
|
||||
config.set(context, key, value)
|
||||
with open(self.config_file, 'w') as file:
|
||||
with open(self.config_file, 'w', encoding="utf-8") as file:
|
||||
config.write(file)
|
||||
|
||||
def config_remove_section(self, context):
|
||||
"""Remove a section from config and save to disk"""
|
||||
if self.loading_config:
|
||||
return
|
||||
config = ConfigParser(interpolation=None)
|
||||
|
|
@ -970,12 +990,12 @@ class MainSettingsWindow():
|
|||
if context in config.sections():
|
||||
config.remove_section(context)
|
||||
else:
|
||||
log.error("Unable to remove section %s" % (context))
|
||||
with open(self.config_file, 'w') as file:
|
||||
log.error("Unable to remove section %s", context)
|
||||
with open(self.config_file, 'w', encoding="utf-8") as file:
|
||||
config.write(file)
|
||||
|
||||
def voice_anchor_float_changed(self, button):
|
||||
self.config_set("main", "floating", "%s" % (button.get_active() == 0))
|
||||
self.config_set("main", "floating", f"{(button.get_active() == 0)}")
|
||||
self.update_floating_anchor()
|
||||
|
||||
def update_floating_anchor(self):
|
||||
|
|
@ -1003,10 +1023,10 @@ class MainSettingsWindow():
|
|||
self.config_set("main", "monitor", plug)
|
||||
|
||||
def voice_align_1_changed(self, button):
|
||||
self.config_set("main", "rightalign", "%s" % (button.get_active()))
|
||||
self.config_set("main", "rightalign", f"{button.get_active()}")
|
||||
|
||||
def voice_align_2_changed(self, button):
|
||||
self.config_set("main", "topalign", "%s" % (button.get_active()))
|
||||
self.config_set("main", "topalign", f"{button.get_active()}")
|
||||
|
||||
def voice_font_changed(self, button):
|
||||
self.config_set("main", "font", button.get_font())
|
||||
|
|
@ -1015,41 +1035,39 @@ class MainSettingsWindow():
|
|||
self.config_set("main", "title_font", button.get_font())
|
||||
|
||||
def voice_icon_spacing_changed(self, button):
|
||||
self.config_set("main", "icon_spacing", "%s" %
|
||||
(int(button.get_value())))
|
||||
self.config_set("main", "icon_spacing", f"{int(button.get_value())}")
|
||||
|
||||
def voice_text_padding_changed(self, button):
|
||||
self.config_set("main", "text_padding", "%s" %
|
||||
(int(button.get_value())))
|
||||
self.config_set("main", "text_padding", f"{int(button.get_value())}")
|
||||
|
||||
def voice_text_vertical_offset_changed(self, button):
|
||||
self.config_set("main", "text_baseline_adj", "%s" %
|
||||
(int(button.get_value())))
|
||||
self.config_set("main", "text_baseline_adj",
|
||||
f"{int(button.get_value())}")
|
||||
|
||||
def voice_vertical_padding_changed(self, button):
|
||||
self.config_set("main", "vert_edge_padding", "%s" %
|
||||
(int(button.get_value())))
|
||||
self.config_set("main", "vert_edge_padding",
|
||||
f"{int(button.get_value())}")
|
||||
|
||||
def voice_horizontal_padding_changed(self, button):
|
||||
self.config_set("main", "horz_edge_padding", "%s" %
|
||||
(int(button.get_value())))
|
||||
self.config_set("main", "horz_edge_padding",
|
||||
f"{int(button.get_value())}")
|
||||
|
||||
def voice_display_horizontally_changed(self, button):
|
||||
self.config_set("main", "horizontal", "%s" % (button.get_active()))
|
||||
self.config_set("main", "horizontal", f"{button.get_active()}")
|
||||
self.set_alignment_labels(button.get_active())
|
||||
|
||||
def voice_highlight_self_changed(self, button):
|
||||
self.config_set("main", "highlight_self", "%s" % (button.get_active()))
|
||||
self.config_set("main", "highlight_self", f"{button.get_active()}")
|
||||
|
||||
def voice_display_speakers_only(self, button):
|
||||
self.config_set("main", "only_speaking", "%s" % (button.get_active()))
|
||||
self.config_set("main", "only_speaking", f"{button.get_active()}")
|
||||
|
||||
def voice_display_speakers_grace_period(self, button):
|
||||
self.config_set("main", "only_speaking_grace", "%s" %
|
||||
(int(button.get_value())))
|
||||
self.config_set("main", "only_speaking_grace",
|
||||
f"{int(button.get_value())}")
|
||||
|
||||
def voice_toggle_test_content(self, button):
|
||||
self.config_set("main", "show_dummy", "%s" % (button.get_active()))
|
||||
self.config_set("main", "show_dummy", f"{button.get_active()}")
|
||||
|
||||
def voice_talking_foreground_changed(self, button):
|
||||
colour = button.get_rgba()
|
||||
|
|
@ -1097,54 +1115,48 @@ class MainSettingsWindow():
|
|||
self.config_set("main", "avatar_bg_col", json.dumps(colour))
|
||||
|
||||
def voice_avatar_opacity_changed(self, button):
|
||||
self.config_set("main", "icon_transparency", "%.2f" %
|
||||
(button.get_value()))
|
||||
self.config_set("main", "icon_transparency",
|
||||
f"{button.get_value():.2f}")
|
||||
|
||||
def voice_avatar_size_changed(self, button):
|
||||
self.config_set("main", "avatar_size", "%s" %
|
||||
(int(button.get_value())))
|
||||
self.config_set("main", "avatar_size", f"{int(button.get_value())}")
|
||||
|
||||
def voice_nick_length_changed(self, button):
|
||||
self.config_set("main", "nick_length", "%s" %
|
||||
(int(button.get_value())))
|
||||
self.config_set("main", "nick_length", f"{int(button.get_value())}")
|
||||
|
||||
def voice_display_icon_only_changed(self, button):
|
||||
self.config_set("main", "icon_only", "%s" % (not button.get_active()))
|
||||
self.config_set("main", "icon_only", f"{(not button.get_active())}")
|
||||
self.voice_show_name_hide_others(button.get_active())
|
||||
|
||||
def voice_square_avatar_changed(self, button):
|
||||
self.config_set("main", "square_avatar", "%s" % (button.get_active()))
|
||||
self.config_set("main", "square_avatar", f"{button.get_active()}")
|
||||
|
||||
def voice_fancy_avatar_shapes_changed(self, button):
|
||||
self.config_set("main", "fancy_border", "%s" % (button.get_active()))
|
||||
self.config_set("main", "fancy_border", f"{button.get_active()}")
|
||||
|
||||
def voice_order_avatars_by_changed(self, button):
|
||||
self.config_set("main", "order", "%s" % (button.get_active()))
|
||||
self.config_set("main", "order", f"{button.get_active()}")
|
||||
|
||||
def voice_border_width_changed(self, button):
|
||||
self.config_set("main", "border_width", "%s" %
|
||||
(int(button.get_value())))
|
||||
self.config_set("main", "border_width", f"{int(button.get_value())}")
|
||||
|
||||
def voice_overflow_style_changed(self, button):
|
||||
self.config_set("main", "overflow", "%s" % (int(button.get_active())))
|
||||
self.config_set("main", "overflow", f"{int(button.get_active())}")
|
||||
|
||||
def voice_show_title_changed(self, button):
|
||||
self.config_set("main", "show_title", "%s" % (button.get_active()))
|
||||
self.config_set("main", "show_title", f"{button.get_active()}")
|
||||
|
||||
def voice_show_connection_status_changed(self, button):
|
||||
self.config_set("main", "show_connection", "%s" %
|
||||
(button.get_active()))
|
||||
self.config_set("main", "show_connection", f"{button.get_active()}")
|
||||
|
||||
def voice_show_disconnected_changed(self, button):
|
||||
self.config_set("main", "show_disconnected", "%s" %
|
||||
(button.get_active()))
|
||||
self.config_set("main", "show_disconnected", f"{button.get_active()}")
|
||||
|
||||
def voice_dummy_count_changed(self, button):
|
||||
self.config_set("main", "dummy_count", "%s" %
|
||||
(int(button.get_value())))
|
||||
self.config_set("main", "dummy_count", f"{int(button.get_value())}")
|
||||
|
||||
def voice_show_avatar_changed(self, button):
|
||||
self.config_set("main", "show_avatar", "%s" % (button.get_active()))
|
||||
self.config_set("main", "show_avatar", f"{button.get_active()}")
|
||||
self.voice_show_avatar_hide_others(button.get_active())
|
||||
|
||||
def voice_show_name_hide_others(self, val):
|
||||
|
|
@ -1176,13 +1188,13 @@ class MainSettingsWindow():
|
|||
self.widget['voice_avatar_opacity'].set_sensitive(False)
|
||||
|
||||
def text_enable_changed(self, button):
|
||||
self.config_set("text", "enabled", "%s" % (button.get_active()))
|
||||
self.config_set("text", "enabled", f"{button.get_active()}")
|
||||
|
||||
def text_popup_style_changed(self, button):
|
||||
self.config_set("text", "popup_style", "%s" % (button.get_active()))
|
||||
self.config_set("text", "popup_style", f"{button.get_active()}")
|
||||
|
||||
def text_popup_time_changed(self, button):
|
||||
self.config_set("text", "text_time", "%s" % (int(button.get_value())))
|
||||
self.config_set("text", "text_time", f"{int(button.get_value())}")
|
||||
|
||||
def text_server_changed(self, button):
|
||||
if button.get_active() < 0:
|
||||
|
|
@ -1219,31 +1231,32 @@ class MainSettingsWindow():
|
|||
def text_monitor_changed(self, button):
|
||||
screen = self.window.get_screen()
|
||||
plug = "Any"
|
||||
monitor = screen.get_monitor_plug_name(button.get_active()-1)
|
||||
monitor = None
|
||||
if button.get_active()>0:
|
||||
monitor = screen.get_monitor_plug_name(button.get_active()-1)
|
||||
if monitor:
|
||||
plug = monitor
|
||||
self.config_set("text", "monitor", plug)
|
||||
|
||||
def text_show_attachments_changed(self, button):
|
||||
self.config_set("text", "show_attach", "%s" % (button.get_active()))
|
||||
self.config_set("text", "show_attach", f"{button.get_active()}")
|
||||
|
||||
def text_line_limit_changed(self, button):
|
||||
self.config_set("text", "line_limit", "%s" % (int(button.get_value())))
|
||||
self.config_set("text", "line_limit", f"{int(button.get_value())}")
|
||||
|
||||
def notification_enable_changed(self, button):
|
||||
self.config_set("notification", "enabled", "%s" %
|
||||
(button.get_active()))
|
||||
self.config_set("notification", "enabled", f"{button.get_active()}")
|
||||
|
||||
def notification_reverse_order_changed(self, button):
|
||||
self.config_set("notification", "rev", "%s" % (button.get_active()))
|
||||
self.config_set("notification", "rev", f"{button.get_active()}")
|
||||
|
||||
def notification_popup_timer_changed(self, button):
|
||||
self.config_set("notification", "text_time", "%s" %
|
||||
(int(button.get_value())))
|
||||
self.config_set("notification", "text_time",
|
||||
f"{int(button.get_value())}")
|
||||
|
||||
def notification_limit_popup_width_changed(self, button):
|
||||
self.config_set("notification", "limit_width", "%s" %
|
||||
(int(button.get_value())))
|
||||
self.config_set("notification", "limit_width",
|
||||
f"{int(button.get_value())}")
|
||||
|
||||
def notification_font_changed(self, button):
|
||||
self.config_set("notification", "font", button.get_font())
|
||||
|
|
@ -1261,46 +1274,44 @@ class MainSettingsWindow():
|
|||
def notification_monitor_changed(self, button):
|
||||
screen = self.window.get_screen()
|
||||
plug = "Any"
|
||||
monitor = screen.get_monitor_plug_name(button.get_active()-1)
|
||||
monitor = None
|
||||
if button.get_active()>0:
|
||||
monitor = screen.get_monitor_plug_name(button.get_active()-1)
|
||||
if monitor:
|
||||
plug = monitor
|
||||
self.config_set("notification", "monitor", plug)
|
||||
|
||||
def notification_align_1_changed(self, button):
|
||||
self.config_set("notification", "rightalign", "%s" %
|
||||
(button.get_active()))
|
||||
self.config_set("notification", "rightalign", f"{button.get_active()}")
|
||||
|
||||
def notification_align_2_changed(self, button):
|
||||
self.config_set("notification", "topalign", "%s" %
|
||||
(button.get_active()))
|
||||
self.config_set("notification", "topalign", f"{button.get_active()}")
|
||||
|
||||
def notification_show_icon(self, button):
|
||||
self.config_set("notification", "show_icon", "%s" %
|
||||
(button.get_active()))
|
||||
self.config_set("notification", "show_icon", f"{button.get_active()}")
|
||||
|
||||
def notification_icon_position_changed(self, button):
|
||||
self.config_set("notification", "icon_left", "%s" %
|
||||
(int(button.get_active() != 1)))
|
||||
self.config_set("notification", "icon_left", f"{
|
||||
int(button.get_active() != 1)}")
|
||||
|
||||
def notification_icon_padding_changed(self, button):
|
||||
self.config_set("notification", "icon_padding", "%s" %
|
||||
(int(button.get_value())))
|
||||
self.config_set("notification", "icon_padding",
|
||||
f"{int(button.get_value())}")
|
||||
|
||||
def notification_icon_size_changed(self, button):
|
||||
self.config_set("notification", "icon_size", "%s" %
|
||||
(int(button.get_value())))
|
||||
self.config_set("notification", "icon_size",
|
||||
f"{int(button.get_value())}")
|
||||
|
||||
def notification_padding_between_changed(self, button):
|
||||
self.config_set("notification", "padding", "%s" %
|
||||
(int(button.get_value())))
|
||||
self.config_set("notification", "padding",
|
||||
f"{int(button.get_value())}")
|
||||
|
||||
def notification_border_radius_changed(self, button):
|
||||
self.config_set("notification", "border_radius", "%s" %
|
||||
(int(button.get_value())))
|
||||
self.config_set("notification", "border_radius",
|
||||
f"{int(button.get_value())}")
|
||||
|
||||
def notification_show_test_content_changed(self, button):
|
||||
self.config_set("notification", "show_dummy", "%s" %
|
||||
(button.get_active()))
|
||||
self.config_set("notification", "show_dummy", f"{button.get_active()}")
|
||||
|
||||
def core_run_on_startup_changed(self, button):
|
||||
self.autostart_helper.set_autostart(button.get_active())
|
||||
|
|
@ -1309,65 +1320,60 @@ class MainSettingsWindow():
|
|||
self.autostart_helper_conf.set_autostart(button.get_active())
|
||||
|
||||
def core_force_xshape_changed(self, button):
|
||||
self.config_set("general", "xshape", "%s" % (button.get_active()))
|
||||
self.config_set("general", "xshape", f"{button.get_active()}")
|
||||
|
||||
def core_show_tray_icon_changed(self, button):
|
||||
self.set_sys_tray_icon_visible(button.get_active())
|
||||
self.config_set("general", "showsystray", "%s" % (button.get_active()))
|
||||
self.config_set("general", "showsystray", f"{button.get_active()}")
|
||||
self.widget['core_settings_min'].set_sensitive(button.get_active())
|
||||
|
||||
def core_hide_overlay_changed(self, button):
|
||||
def core_hide_overlay_changed(self, _button):
|
||||
self.toggle_overlay()
|
||||
|
||||
def core_settings_min_changed(self, button):
|
||||
self.config_set("general", "start_min", "%s" % (button.get_active()))
|
||||
self.config_set("general", "start_min", f"{button.get_active()}")
|
||||
|
||||
def core_reset_all(self, button):
|
||||
def core_reset_all(self, _button):
|
||||
self.config_remove_section("general")
|
||||
self.read_config()
|
||||
|
||||
def voice_reset_all(self, button):
|
||||
def voice_reset_all(self, _button):
|
||||
self.config_remove_section("main")
|
||||
self.read_config()
|
||||
|
||||
def text_reset_all(self, button):
|
||||
def text_reset_all(self, _button):
|
||||
self.config_remove_section("text")
|
||||
self.read_config()
|
||||
|
||||
def notification_reset_all(self, button):
|
||||
def notification_reset_all(self, _button):
|
||||
self.config_remove_section("notification")
|
||||
self.read_config()
|
||||
|
||||
def voice_hide_mouseover_changed(self, button):
|
||||
self.config_set("main", "autohide", "%s" % (button.get_active()))
|
||||
self.config_set("main", "autohide", f"{button.get_active()}")
|
||||
|
||||
def text_hide_mouseover_changed(self, button):
|
||||
self.config_set("text", "autohide", "%s" % (button.get_active()))
|
||||
self.config_set("text", "autohide", f"{button.get_active()}")
|
||||
|
||||
def voice_mouseover_timeout_changed(self, button):
|
||||
self.config_set("main", "autohide_timer", "%s" %
|
||||
(int(button.get_value())))
|
||||
self.config_set("main", "autohide_timer", f"{int(button.get_value())}")
|
||||
|
||||
def text_mouseover_timeout_changed(self, button):
|
||||
self.config_set("text", "autohide_timer", "%s" %
|
||||
(int(button.get_value())))
|
||||
self.config_set("text", "autohide_timer", f"{int(button.get_value())}")
|
||||
|
||||
def inactive_fade_changed(self, button):
|
||||
self.config_set("main", "fade_out_inactive", "%s" %
|
||||
(button.get_active()))
|
||||
self.config_set("main", "fade_out_inactive", f"{button.get_active()}")
|
||||
|
||||
def inactive_fade_opacity_changed(self, button):
|
||||
self.config_set("main", "fade_out_limit", "%.2f" %
|
||||
(button.get_value()))
|
||||
self.config_set("main", "fade_out_limit",
|
||||
f"{button.get_value():.2f}")
|
||||
|
||||
def inactive_time_changed(self, button):
|
||||
self.config_set("main", "inactive_time", "%s" %
|
||||
(int(button.get_value())))
|
||||
self.config_set("main", "inactive_time", f"{int(button.get_value())}")
|
||||
|
||||
def inactive_fade_time_changed(self, button):
|
||||
self.config_set("main", "inactive_fade_time", "%s" %
|
||||
(int(button.get_value())))
|
||||
self.config_set("main", "inactive_fade_time",
|
||||
f"{int(button.get_value())}")
|
||||
|
||||
def core_audio_assist_changed(self, button):
|
||||
self.config_set("general", "audio_assist", "%s" %
|
||||
(button.get_active()))
|
||||
self.config_set("general", "audio_assist", f"{button.get_active()}")
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ from .overlay import OverlayWindow
|
|||
gi.require_version("Gtk", "3.0")
|
||||
gi.require_version('PangoCairo', '1.0')
|
||||
# pylint: disable=wrong-import-position,wrong-import-order
|
||||
from gi.repository import Pango, PangoCairo, GLib # nopep8
|
||||
from gi.repository import Pango, PangoCairo # nopep8
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -56,10 +56,12 @@ class TextOverlayWindow(OverlayWindow):
|
|||
self.redraw()
|
||||
|
||||
def set_blank(self):
|
||||
""" Set contents blank and redraw """
|
||||
self.content = []
|
||||
self.set_needs_redraw()
|
||||
|
||||
def tick(self):
|
||||
""" Check for old images """
|
||||
if len(self.attachment) > self.line_limit:
|
||||
# We've probably got old images!
|
||||
oldlist = self.attachment
|
||||
|
|
@ -72,57 +74,43 @@ class TextOverlayWindow(OverlayWindow):
|
|||
self.attachment[url] = oldlist[url]
|
||||
|
||||
def set_text_time(self, timer):
|
||||
"""
|
||||
Set the duration that a message will be visible for.
|
||||
"""
|
||||
"""Config option: Time before messages disappear from overlay"""
|
||||
if self.text_time != timer or self.timer_after_draw != timer:
|
||||
self.text_time = timer
|
||||
self.timer_after_draw = timer
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_text_list(self, tlist, altered):
|
||||
"""
|
||||
Update the list of text messages to show
|
||||
"""
|
||||
"""Change contents of overlay"""
|
||||
self.content = tlist[-self.line_limit:]
|
||||
if altered:
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_fg(self, fg_col):
|
||||
"""
|
||||
Set default text colour
|
||||
"""
|
||||
"""Config option: Sets the text colour"""
|
||||
if self.fg_col != fg_col:
|
||||
self.fg_col = fg_col
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_bg(self, bg_col):
|
||||
"""
|
||||
Set background colour
|
||||
"""
|
||||
"""Config option: Set the background colour"""
|
||||
if self.bg_col != bg_col:
|
||||
self.bg_col = bg_col
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_show_attach(self, attachment):
|
||||
"""
|
||||
Set if attachments should be shown inline
|
||||
"""
|
||||
"""Config option: Show image attachments"""
|
||||
if self.attachment != attachment:
|
||||
self.show_attach = attachment
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_popup_style(self, boolean):
|
||||
"""
|
||||
Set if message disappear after a certain duration
|
||||
"""
|
||||
"""Config option: Messages should disappear after being shown for some time"""
|
||||
if self.popup_style != boolean:
|
||||
self.popup_style = boolean
|
||||
|
||||
def set_font(self, font):
|
||||
"""
|
||||
Set font used to render text
|
||||
"""
|
||||
"""Config option: Set font used for rendering"""
|
||||
if self.text_font != font:
|
||||
self.text_font = font
|
||||
|
||||
|
|
@ -133,28 +121,24 @@ class TextOverlayWindow(OverlayWindow):
|
|||
self.set_needs_redraw()
|
||||
|
||||
def set_line_limit(self, limit):
|
||||
"""
|
||||
Change maximum number of lines in overlay
|
||||
"""
|
||||
"""Config option: Limit number of lines rendered"""
|
||||
if self.line_limit != limit:
|
||||
self.line_limit = limit
|
||||
|
||||
def make_line(self, message):
|
||||
"""
|
||||
Decode a recursive JSON object into pango markup.
|
||||
"""
|
||||
"""Decode a recursive JSON object into pango markup."""
|
||||
ret = ""
|
||||
if isinstance(message, list):
|
||||
for inner_message in message:
|
||||
ret = "%s%s" % (ret, self.make_line(inner_message))
|
||||
ret = f"{ret}{self.make_line(inner_message)}"
|
||||
elif isinstance(message, str):
|
||||
ret = self.sanitize_string(message)
|
||||
elif message['type'] == 'strong':
|
||||
ret = "<b>%s</b>" % (self.make_line(message['content']))
|
||||
ret = f"<b>{self.make_line(message['content'])}</b>"
|
||||
elif message['type'] == 'text':
|
||||
ret = self.sanitize_string(message['content'])
|
||||
elif message['type'] == 'link':
|
||||
ret = "<u>%s</u>" % (self.make_line(message['content']))
|
||||
ret = f"<u>{self.make_line(message['content'])}</u>"
|
||||
elif message['type'] == 'emoji':
|
||||
if 'surrogate' in message:
|
||||
# ['src'] is SVG URL
|
||||
|
|
@ -163,20 +147,21 @@ class TextOverlayWindow(OverlayWindow):
|
|||
else:
|
||||
### Add Image ###
|
||||
self.image_list.append(
|
||||
f"https://cdn.discordapp.com/emojis/{message['emojiId']}.png?v=1"
|
||||
f"https://cdn.discordapp.com/emojis/{
|
||||
message['emojiId']}.png?v=1"
|
||||
)
|
||||
ret = "`"
|
||||
elif (message['type'] == 'inlineCode' or
|
||||
message['type'] == 'codeBlock' or
|
||||
message['type'] == 'blockQuote'):
|
||||
ret = "<span font_family=\"monospace\" background=\"#0004\">%s</span>" % (
|
||||
self.make_line(message['content']))
|
||||
ret = f"<span font_family=\"monospace\" background=\"#0004\">{
|
||||
self.make_line(message['content'])}</span>"
|
||||
elif message['type'] == 'u':
|
||||
ret = "<u>%s</u>" % (self.make_line(message['content']))
|
||||
ret = f"<u>{self.make_line(message['content'])}</u>"
|
||||
elif message['type'] == 'em':
|
||||
ret = "<i>%s</i>" % (self.make_line(message['content']))
|
||||
ret = f"<i>{self.make_line(message['content'])}</i>"
|
||||
elif message['type'] == 's':
|
||||
ret = "<s>%s</s>" % (self.make_line(message['content']))
|
||||
ret = f"<s>{self.make_line(message['content'])}</s>"
|
||||
elif message['type'] == 'channel':
|
||||
ret = self.make_line(message['content'])
|
||||
elif message['type'] == 'mention':
|
||||
|
|
@ -189,14 +174,13 @@ class TextOverlayWindow(OverlayWindow):
|
|||
self.warned_filetypes.append(message['type'])
|
||||
return ret
|
||||
|
||||
def recv_attach(self, identifier, pix, mask):
|
||||
"""
|
||||
Called when an image has been downloaded by image_getter
|
||||
"""
|
||||
def recv_attach(self, identifier, pix, _mask):
|
||||
"""Callback from image_getter"""
|
||||
self.attachment[identifier] = pix
|
||||
self.set_needs_redraw()
|
||||
|
||||
def has_content(self):
|
||||
"""Returns true if overlay has meaningful content to render"""
|
||||
if self.piggyback and self.piggyback.has_content():
|
||||
return True
|
||||
if not self.enabled:
|
||||
|
|
@ -206,15 +190,12 @@ class TextOverlayWindow(OverlayWindow):
|
|||
return self.content
|
||||
|
||||
def overlay_draw(self, w, context, data=None):
|
||||
"""
|
||||
Draw the overlay
|
||||
"""
|
||||
"""Draw the overlay"""
|
||||
if self.piggyback:
|
||||
self.piggyback.overlay_draw(w, context, data)
|
||||
if not self.enabled:
|
||||
return
|
||||
self.context = context
|
||||
(width, height) = self.get_size()
|
||||
if not self.piggyback_parent:
|
||||
context.set_antialias(cairo.ANTIALIAS_GOOD)
|
||||
context.set_source_rgba(0.0, 0.0, 0.0, 0.0)
|
||||
|
|
@ -234,7 +215,6 @@ class TextOverlayWindow(OverlayWindow):
|
|||
context.translate(floating_x, floating_y)
|
||||
context.rectangle(0, 0, floating_width, floating_height)
|
||||
context.clip()
|
||||
pass
|
||||
(floating_x, floating_y, floating_width,
|
||||
floating_height) = self.get_floating_coords()
|
||||
current_y = floating_height
|
||||
|
|
@ -249,7 +229,7 @@ class TextOverlayWindow(OverlayWindow):
|
|||
if 'nick_col' in line and line['nick_col']:
|
||||
col = line['nick_col']
|
||||
for in_line in line['content']:
|
||||
out_line = "%s%s" % (out_line, self.make_line(in_line))
|
||||
out_line = f"{out_line}{self.make_line(in_line)}"
|
||||
if line['attach'] and self.show_attach:
|
||||
attachment = line['attach'][0]
|
||||
url = attachment['url']
|
||||
|
|
@ -267,10 +247,8 @@ class TextOverlayWindow(OverlayWindow):
|
|||
else:
|
||||
log.warning("Unknown file extension '%s'", extension)
|
||||
# cy = self.draw_text(cy, "%s" % (line['attach']))
|
||||
message = "<span foreground='%s'>%s</span>: %s" % (self.sanitize_string(col),
|
||||
self.sanitize_string(
|
||||
line["nick"]),
|
||||
out_line)
|
||||
message = f"<span foreground='{self.sanitize_string(col)}'>{self.sanitize_string(
|
||||
line["nick"])}</span>: {out_line}"
|
||||
current_y = self.draw_text(current_y, message)
|
||||
if current_y <= 0:
|
||||
# We've done enough
|
||||
|
|
@ -279,10 +257,8 @@ class TextOverlayWindow(OverlayWindow):
|
|||
self.context = None
|
||||
|
||||
def draw_attach(self, pos_y, url):
|
||||
"""
|
||||
Draw an attachment
|
||||
"""
|
||||
(floating_x, floating_y, floating_width,
|
||||
"""Draw an attachment"""
|
||||
(_floating_x, _floating_y, floating_width,
|
||||
floating_height) = self.get_floating_coords()
|
||||
if url in self.attachment and self.attachment[url]:
|
||||
pix = self.attachment[url]
|
||||
|
|
@ -302,16 +278,14 @@ class TextOverlayWindow(OverlayWindow):
|
|||
return pos_y
|
||||
|
||||
def draw_text(self, pos_y, text):
|
||||
"""
|
||||
Draw a text message, returning the Y position of the next message
|
||||
"""
|
||||
"""Draw a text message, returning the Y position of the next message"""
|
||||
layout = self.create_pango_layout(text)
|
||||
layout.set_auto_dir(True)
|
||||
layout.set_markup(text, -1)
|
||||
attr = layout.get_attributes()
|
||||
|
||||
(floating_x, floating_y, floating_width,
|
||||
floating_height) = self.get_floating_coords()
|
||||
(_floating_x, _floating_y, floating_width,
|
||||
_floating_height) = self.get_floating_coords()
|
||||
layout.set_width(Pango.SCALE * floating_width)
|
||||
layout.set_spacing(Pango.SCALE * 3)
|
||||
if self.text_font:
|
||||
|
|
@ -351,11 +325,9 @@ class TextOverlayWindow(OverlayWindow):
|
|||
return pos_y - text_height
|
||||
|
||||
def render_custom(self, ctx, shape, path, _data):
|
||||
"""
|
||||
Draw an inline image as a custom emoticon
|
||||
"""
|
||||
"""Draw an inline image as a custom emoticon"""
|
||||
if shape.data >= len(self.image_list):
|
||||
log.warning(f"{shape.data} >= {len(self.image_list)}")
|
||||
log.warning("%s >= %s", shape.data, len(self.image_list))
|
||||
return
|
||||
# key is the url to the image
|
||||
key = self.image_list[shape.data]
|
||||
|
|
@ -371,9 +343,7 @@ class TextOverlayWindow(OverlayWindow):
|
|||
return True
|
||||
|
||||
def sanitize_string(self, string):
|
||||
"""
|
||||
Sanitize a text message so that it doesn't intefere with Pango's XML format
|
||||
"""
|
||||
"""Sanitize a text message so that it doesn't intefere with Pango's XML format"""
|
||||
string = string.replace("&", "&")
|
||||
string = string.replace("<", "<")
|
||||
string = string .replace(">", ">")
|
||||
|
|
|
|||
|
|
@ -15,16 +15,15 @@ import random
|
|||
import gettext
|
||||
import logging
|
||||
import math
|
||||
import cairo
|
||||
import sys
|
||||
import locale
|
||||
import pkg_resources
|
||||
from time import perf_counter
|
||||
import cairo
|
||||
import pkg_resources
|
||||
from .overlay import OverlayWindow
|
||||
from .image_getter import get_surface, draw_img_to_rect, draw_img_to_mask
|
||||
# pylint: disable=wrong-import-order
|
||||
import gi
|
||||
gi.require_version("Gtk", "3.0")
|
||||
gi.require_version('PangoCairo', '1.0')
|
||||
# pylint: disable=wrong-import-position,wrong-import-order
|
||||
from gi.repository import Pango, PangoCairo, GLib # nopep8
|
||||
|
|
@ -45,9 +44,6 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
self.avatars = {}
|
||||
self.avatar_masks = {}
|
||||
|
||||
# Cache for when somebody last spoke, used for "only_speaking" grace period
|
||||
self.speaker_cache = {}
|
||||
|
||||
self.dummy_data = []
|
||||
mostly_false = [False, False, False, False, False, False, False, True]
|
||||
for i in range(0, 100):
|
||||
|
|
@ -55,12 +51,11 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
scream = ''
|
||||
if random.randint(0, 20) == 2:
|
||||
scream = random.randint(8, 15)*'a'
|
||||
name = "Player %d %s" % (i, scream)
|
||||
name = f"Player {i} {scream}"
|
||||
self.dummy_data.append({
|
||||
"id": i,
|
||||
"username": name,
|
||||
"avatar": None,
|
||||
"mute": False,
|
||||
"deaf": mostly_false[random.randint(0, 7)],
|
||||
"mute": mostly_false[random.randint(0, 7)],
|
||||
"speaking": speaking,
|
||||
|
|
@ -82,6 +77,7 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
self.highlight_self = None
|
||||
self.order = None
|
||||
self.def_avatar = None
|
||||
self.def_avatar_mask = None
|
||||
self.channel_icon = None
|
||||
self.channel_mask = None
|
||||
self.channel_icon_url = None
|
||||
|
|
@ -95,6 +91,7 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
self.border_width = 2
|
||||
self.icon_transparency = 0.0
|
||||
self.fancy_border = False
|
||||
self.only_speaking_grace_period = 0
|
||||
|
||||
self.fade_out_inactive = True
|
||||
self.fade_out_limit = 0.1
|
||||
|
|
@ -130,7 +127,7 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
self.redraw()
|
||||
|
||||
def reset_action_timer(self):
|
||||
# Reset time since last voice activity
|
||||
"""Reset time since last voice activity"""
|
||||
self.fade_opacity = 1.0
|
||||
|
||||
# Remove both fading-out effect and timer set last time this happened
|
||||
|
|
@ -141,13 +138,13 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
GLib.source_remove(self.fadeout_timeout)
|
||||
self.fadeout_timeout = None
|
||||
|
||||
# If we're using this feature, schedule a new iactivity timer
|
||||
# If we're using this feature, schedule a new inactivity timer
|
||||
if self.fade_out_inactive:
|
||||
self.inactive_timeout = GLib.timeout_add_seconds(
|
||||
self.inactive_time, self.overlay_inactive)
|
||||
|
||||
def overlay_inactive(self):
|
||||
# Inactivity has hit the first threshold, start fading out
|
||||
"""Timed callback when inactivity limit is hit"""
|
||||
self.fade_start = perf_counter()
|
||||
# Fade out in 200 steps over X seconds.
|
||||
self.fadeout_timeout = GLib.timeout_add(
|
||||
|
|
@ -156,8 +153,10 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
return False
|
||||
|
||||
def overlay_fadeout(self):
|
||||
"""Repeated callback after inactivity started"""
|
||||
self.set_needs_redraw()
|
||||
# There's no guarantee over the granularity of the callback here, so use our time-since to work out how faded out we should be
|
||||
# There's no guarantee over the granularity of the callback here,
|
||||
# so use our time-since to work out how faded out we should be
|
||||
# Might look choppy on systems under high cpu usage but that's just how it's going to be
|
||||
now = perf_counter()
|
||||
time_percent = (now - self.fade_start) / self.inactive_fade_time
|
||||
|
|
@ -171,21 +170,22 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
return True
|
||||
|
||||
def col(self, col, alpha=1.0):
|
||||
"""
|
||||
Convenience function to set the cairo context next colour. Altered to account for fade-out function
|
||||
"""
|
||||
if alpha == None:
|
||||
"""Convenience function to set the cairo context next colour.
|
||||
Altered to account for fade-out function"""
|
||||
if alpha is None:
|
||||
self.context.set_source_rgba(col[0], col[1], col[2], col[3])
|
||||
else:
|
||||
self.context.set_source_rgba(
|
||||
col[0], col[1], col[2], col[3] * alpha * self.fade_opacity)
|
||||
|
||||
def set_icon_transparency(self, trans):
|
||||
"""Config option: icon transparency"""
|
||||
if self.icon_transparency != trans:
|
||||
self.icon_transparency = trans
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_blank(self):
|
||||
"""Set data to blank and redraw"""
|
||||
self.userlist = []
|
||||
self.channel_icon = None
|
||||
self.channel_icon_url = None
|
||||
|
|
@ -194,7 +194,9 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
self.set_needs_redraw()
|
||||
|
||||
def set_fade_out_inactive(self, enabled, fade_time, fade_duration, fade_to):
|
||||
if self.fade_out_inactive != enabled or self.inactive_time != fade_time or self.inactive_fade_time != fade_duration or self.fade_out_limit != fade_to:
|
||||
"""Config option: fade out options"""
|
||||
if (self.fade_out_inactive != enabled or self.inactive_time != fade_time or
|
||||
self.inactive_fade_time != fade_duration or self.fade_out_limit != fade_to):
|
||||
self.fade_out_inactive = enabled
|
||||
self.inactive_time = fade_time
|
||||
self.inactive_fade_time = fade_duration
|
||||
|
|
@ -202,288 +204,235 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
self.reset_action_timer()
|
||||
|
||||
def set_title_font(self, font):
|
||||
"""Config option: font used to render title"""
|
||||
if self.title_font != font:
|
||||
self.title_font = font
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_show_connection(self, show_connection):
|
||||
"""Config option: show connection status alongside users"""
|
||||
if self.show_connection != show_connection:
|
||||
self.show_connection = show_connection
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_show_avatar(self, show_avatar):
|
||||
"""Config option: show avatar icons"""
|
||||
if self.show_avatar != show_avatar:
|
||||
self.show_avatar = show_avatar
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_show_title(self, show_title):
|
||||
"""Config option: show channel title alongside users"""
|
||||
if self.show_title != show_title:
|
||||
self.show_title = show_title
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_show_disconnected(self, show_disconnected):
|
||||
"""Config option: show even when disconnected from voice chat"""
|
||||
if self.show_disconnected != show_disconnected:
|
||||
self.show_disconnected = show_disconnected
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_show_dummy(self, show_dummy):
|
||||
"""
|
||||
Toggle use of dummy userdata to help choose settings
|
||||
"""
|
||||
"""Config option: Show placeholder information"""
|
||||
if self.use_dummy != show_dummy:
|
||||
self.use_dummy = show_dummy
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_dummy_count(self, dummy_count):
|
||||
"""Config option: Change the count of placeholders"""
|
||||
if self.dummy_count != dummy_count:
|
||||
self.dummy_count = dummy_count
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_overflow(self, overflow):
|
||||
"""
|
||||
How should excessive numbers of users be dealt with?
|
||||
"""
|
||||
def set_overflow_style(self, overflow):
|
||||
"""Config option: Change handling of too many users to render"""
|
||||
if self.overflow != overflow:
|
||||
self.overflow = overflow
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_bg(self, background_colour):
|
||||
"""
|
||||
Set the background colour
|
||||
"""
|
||||
"""Config option: Set background colour. Used to draw the transparent window.
|
||||
Should not be changed as then the entire screen is obscured"""
|
||||
if self.norm_col != background_colour:
|
||||
self.norm_col = background_colour
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_fg(self, foreground_colour):
|
||||
"""
|
||||
Set the text colour
|
||||
"""
|
||||
"""Config option: Set foreground colour. Used to render text"""
|
||||
if self.text_col != foreground_colour:
|
||||
self.text_col = foreground_colour
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_tk(self, talking_colour):
|
||||
"""
|
||||
Set the border colour for users who are talking
|
||||
"""
|
||||
"""Config option: Set talking border colour.
|
||||
Used to render border around users who are talking"""
|
||||
if self.talk_col != talking_colour:
|
||||
self.talk_col = talking_colour
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_mt(self, mute_colour):
|
||||
"""
|
||||
Set the colour of mute and deafen logos
|
||||
"""
|
||||
"""Config option: Set mute colour. Used to render mute and deaf images"""
|
||||
if self.mute_col != mute_colour:
|
||||
self.mute_col = mute_colour
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_mute_bg(self, mute_bg_col):
|
||||
"""
|
||||
Set the background colour for mute/deafen icon
|
||||
"""
|
||||
"""Config option: Set mute background colour.
|
||||
Used to tint the user avatar before rendering the mute or deaf image above it"""
|
||||
if self.mute_bg_col != mute_bg_col:
|
||||
self.mute_bg_col = mute_bg_col
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_avatar_bg_col(self, avatar_bg_col):
|
||||
"""
|
||||
Set Avatar background colour
|
||||
"""
|
||||
"""Config option: Set avatar background colour.
|
||||
Drawn before user avatar but only visible if default fallback avatar can't be found"""
|
||||
if self.avatar_bg_col != avatar_bg_col:
|
||||
self.avatar_bg_col = avatar_bg_col
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_hi(self, highlight_colour):
|
||||
"""
|
||||
Set the colour of background for speaking users
|
||||
"""
|
||||
"""Config option: Set talking background colour.
|
||||
Used to render the background behind users name."""
|
||||
if self.hili_col != highlight_colour:
|
||||
self.hili_col = highlight_colour
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_fg_hi(self, highlight_colour):
|
||||
"""
|
||||
Set the colour of background for speaking users
|
||||
"""
|
||||
"""Config option: Set talking text colour.
|
||||
Used to render the usernames of users who are talking"""
|
||||
if self.text_hili_col != highlight_colour:
|
||||
self.text_hili_col = highlight_colour
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_bo(self, border_colour):
|
||||
"""
|
||||
Set the colour for idle border
|
||||
"""
|
||||
"""Config option: Set border colour. Used to render border around users"""
|
||||
if self.border_col != border_colour:
|
||||
self.border_col = border_colour
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_avatar_size(self, size):
|
||||
"""
|
||||
Set the size of the avatar icons
|
||||
"""
|
||||
"""Config option: Set avatar size in window-space pixels"""
|
||||
if self.avatar_size != size:
|
||||
self.avatar_size = size
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_nick_length(self, size):
|
||||
"""
|
||||
Set the length of nickname
|
||||
"""
|
||||
"""Config option: Limit username length"""
|
||||
if self.nick_length != size:
|
||||
self.nick_length = size
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_icon_spacing(self, i):
|
||||
"""
|
||||
Set the spacing between avatar icons
|
||||
"""
|
||||
"""Config option: Space between users in the list, in window-space pixels"""
|
||||
if self.icon_spacing != i:
|
||||
self.icon_spacing = i
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_text_padding(self, i):
|
||||
"""
|
||||
Set padding between text and border
|
||||
"""
|
||||
"""Config option: Space between user avatar and username, in window-space pixels"""
|
||||
if self.text_pad != i:
|
||||
self.text_pad = i
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_text_baseline_adj(self, i):
|
||||
"""
|
||||
Set padding between text and border
|
||||
"""
|
||||
"""Config option: Vertical offset used to render all text, in window-space pixels"""
|
||||
if self.text_baseline_adj != i:
|
||||
self.text_baseline_adj = i
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_vert_edge_padding(self, i):
|
||||
"""
|
||||
Set padding between top/bottom of screen and overlay contents
|
||||
"""
|
||||
"""Config option: Vertical offset from edge of window, in window-space pixels"""
|
||||
if self.vert_edge_padding != i:
|
||||
self.vert_edge_padding = i
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_horz_edge_padding(self, i):
|
||||
"""
|
||||
Set padding between left/right of screen and overlay contents
|
||||
"""
|
||||
"""Config option: Horizontal offset from edge of window, in window-space pixels"""
|
||||
if self.horz_edge_padding != i:
|
||||
self.horz_edge_padding = i
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_square_avatar(self, i):
|
||||
"""
|
||||
Set if the overlay should crop avatars to a circle or show full square image
|
||||
"""
|
||||
"""Config option: Mask avatar with a circle before rendering"""
|
||||
if self.round_avatar == i:
|
||||
self.round_avatar = not i
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_fancy_border(self, border):
|
||||
"""
|
||||
Sets if border should wrap around non-square avatar images
|
||||
"""
|
||||
"""Config option: Use transparent edges of image as border,
|
||||
instead of mask (square/circle)"""
|
||||
if self.fancy_border != border:
|
||||
self.fancy_border = border
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_only_speaking(self, only_speaking):
|
||||
"""
|
||||
Set if overlay should only show people who are talking
|
||||
"""
|
||||
"""Config option: Filter user list to only those who
|
||||
are talking and those who have stopped talking recently"""
|
||||
if self.only_speaking != only_speaking:
|
||||
self.only_speaking = only_speaking
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_only_speaking_grace_period(self, grace_period):
|
||||
"""
|
||||
Set grace period before hiding people who are not talking
|
||||
"""
|
||||
"""Config option: How long after stopping speaking the user remains shown"""
|
||||
self.only_speaking_grace_period = grace_period
|
||||
self.timer_after_draw = grace_period
|
||||
|
||||
def set_highlight_self(self, highlight_self):
|
||||
"""
|
||||
Set if the overlay should highlight the user
|
||||
"""
|
||||
"""Config option: Local User should be kept at top of list"""
|
||||
if self.highlight_self != highlight_self:
|
||||
self.highlight_self = highlight_self
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_order(self, i):
|
||||
"""
|
||||
Set the method used to order avatar icons & names
|
||||
"""
|
||||
"""Config option: Set method used to order user list"""
|
||||
if self.order != i:
|
||||
self.order = i
|
||||
self.sort_list(self.userlist)
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_icon_only(self, i):
|
||||
"""
|
||||
Set if the overlay should draw only the icon
|
||||
"""
|
||||
"""Config option: Show only the avatar, without text or its background"""
|
||||
if self.icon_only != i:
|
||||
self.icon_only = i
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_border_width(self, width):
|
||||
def set_drawn_border_width(self, width):
|
||||
"""Config option: Set width of border around username and avatar"""
|
||||
if self.border_width != width:
|
||||
self.border_width = width
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_horizontal(self, horizontal=False):
|
||||
"""Config option: Userlist should be drawn horizontally"""
|
||||
if self.horizontal != horizontal:
|
||||
self.horizontal = horizontal
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_guild_ids(self, guild_ids=tuple()):
|
||||
if self.discover.connection:
|
||||
for _id in guild_ids:
|
||||
if _id not in self.guild_ids:
|
||||
self.discover.connection.req_channels(_id)
|
||||
self.guild_ids = guild_ids
|
||||
|
||||
def set_wind_col(self):
|
||||
"""
|
||||
Use window colour to draw
|
||||
"""
|
||||
"""Use window colour to draw"""
|
||||
self.col(self.wind_col, None)
|
||||
|
||||
def set_norm_col(self):
|
||||
"""
|
||||
Use background colour to draw
|
||||
"""
|
||||
"""Use background colour to draw"""
|
||||
self.col(self.norm_col)
|
||||
|
||||
def set_talk_col(self, alpha=1.0):
|
||||
"""
|
||||
Use talking colour to draw
|
||||
"""
|
||||
"""Use talking colour to draw"""
|
||||
self.col(self.talk_col, alpha)
|
||||
|
||||
def set_mute_col(self):
|
||||
"""
|
||||
Use mute colour to draw
|
||||
"""
|
||||
"""Use mute colour to draw"""
|
||||
self.col(self.mute_col)
|
||||
|
||||
def set_channel_title(self, channel_title):
|
||||
"""
|
||||
Set title above voice list
|
||||
"""
|
||||
"""Set title above voice list"""
|
||||
if self.channel_title != channel_title:
|
||||
self.channel_title = channel_title
|
||||
self.set_needs_redraw()
|
||||
|
||||
def set_channel_icon(self, url):
|
||||
"""
|
||||
Change the icon for channel
|
||||
"""
|
||||
"""Change the icon for channel"""
|
||||
if not url:
|
||||
self.channel_icon = None
|
||||
self.channel_icon_url = None
|
||||
|
|
@ -493,9 +442,7 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
self.channel_icon_url = url
|
||||
|
||||
def set_user_list(self, userlist, alt):
|
||||
"""
|
||||
Set the users in list to draw
|
||||
"""
|
||||
"""Set the users in list to draw"""
|
||||
self.userlist = userlist
|
||||
for user in userlist:
|
||||
if "nick" in user:
|
||||
|
|
@ -508,14 +455,13 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
self.set_needs_redraw()
|
||||
|
||||
def set_connection_status(self, connection):
|
||||
"""
|
||||
Set if discord has a clean connection to server
|
||||
"""
|
||||
"""Set if discord has a clean connection to server"""
|
||||
if self.connection_status != connection['state']:
|
||||
self.connection_status = connection['state']
|
||||
self.set_needs_redraw()
|
||||
|
||||
def sort_list(self, in_list):
|
||||
"""Take a userlist and sort it according to config option"""
|
||||
if self.order == 1: # ID Sort
|
||||
in_list.sort(key=lambda x: x["id"])
|
||||
elif self.order == 2: # Spoken sort
|
||||
|
|
@ -526,6 +472,7 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
return in_list
|
||||
|
||||
def has_content(self):
|
||||
"""Returns true if overlay has meaningful content to render"""
|
||||
if not self.enabled:
|
||||
return False
|
||||
if self.hidden:
|
||||
|
|
@ -535,9 +482,7 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
return self.userlist
|
||||
|
||||
def overlay_draw(self, w, context, data=None):
|
||||
"""
|
||||
Draw the Overlay
|
||||
"""
|
||||
"""Draw the Overlay"""
|
||||
self.context = context
|
||||
context.set_antialias(cairo.ANTIALIAS_GOOD)
|
||||
# Get size of window
|
||||
|
|
@ -566,7 +511,8 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
context.clip()
|
||||
|
||||
context.set_operator(cairo.OPERATOR_OVER)
|
||||
if not self.show_disconnected and self.connection_status == "DISCONNECTED" and not self.use_dummy:
|
||||
if (not self.show_disconnected and self.connection_status == "DISCONNECTED"
|
||||
and not self.use_dummy):
|
||||
return
|
||||
|
||||
connection = self.discover.connection
|
||||
|
|
@ -580,6 +526,8 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
if self.use_dummy: # Sorting every frame is an awful idea. Maybe put this off elsewhere?
|
||||
users_to_draw = self.sort_list(self.dummy_data[0:self.dummy_count])
|
||||
userlist = self.dummy_data
|
||||
now = perf_counter()
|
||||
|
||||
for user in userlist:
|
||||
# Bad object equality here, so we need to reassign
|
||||
if "id" in self_user and user["id"] == self_user["id"]:
|
||||
|
|
@ -595,17 +543,16 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
if self.only_speaking:
|
||||
speaking = "speaking" in user and user["speaking"]
|
||||
|
||||
# Update the speaker cache
|
||||
# Extend timer if mid-speaking
|
||||
if speaking:
|
||||
self.speaker_cache[user["username"]] = perf_counter()
|
||||
|
||||
user['lastspoken'] = perf_counter()
|
||||
if not speaking:
|
||||
grace = self.only_speaking_grace_period
|
||||
|
||||
if (
|
||||
grace > 0
|
||||
and (last_spoke := self.speaker_cache.get(user["username"]))
|
||||
and (perf_counter() - last_spoke) < grace
|
||||
and (last_spoke := user['lastspoken'])
|
||||
and (now - last_spoke) < grace
|
||||
):
|
||||
# The user spoke within the grace period, so don't hide
|
||||
# them just yet
|
||||
|
|
@ -624,14 +571,14 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
avatars_per_row = sys.maxsize
|
||||
|
||||
# Calculate height needed to show overlay
|
||||
doTitle = False
|
||||
doConnection = False
|
||||
do_title = False
|
||||
do_connection = False
|
||||
if self.show_connection:
|
||||
users_to_draw.insert(0, None)
|
||||
doConnection = True
|
||||
do_connection = True
|
||||
if self.show_title and self.channel_title:
|
||||
users_to_draw.insert(0, None)
|
||||
doTitle = True
|
||||
do_title = True
|
||||
|
||||
if self.horizontal:
|
||||
needed_width = (len(users_to_draw) * line_height) + \
|
||||
|
|
@ -655,7 +602,7 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
rows_to_draw = []
|
||||
while len(users_to_draw) > 0:
|
||||
row = []
|
||||
for i in range(0, min(avatars_per_row, len(users_to_draw))):
|
||||
for _i in range(0, min(avatars_per_row, len(users_to_draw))):
|
||||
row.append(users_to_draw.pop(0))
|
||||
rows_to_draw.append(row)
|
||||
for row in rows_to_draw:
|
||||
|
|
@ -668,14 +615,14 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
|
||||
for user in row:
|
||||
if not user:
|
||||
if doTitle:
|
||||
doTitle = False
|
||||
if do_title:
|
||||
do_title = False
|
||||
text_width = self.draw_title(
|
||||
context, current_x, current_y, avatar_size, line_height)
|
||||
elif doConnection:
|
||||
elif do_connection:
|
||||
text_width = self.draw_connection(
|
||||
context, current_x, current_y, avatar_size, line_height)
|
||||
doConnection = False
|
||||
do_connection = False
|
||||
else:
|
||||
self.draw_avatar(context, user, current_x,
|
||||
current_y, avatar_size, line_height)
|
||||
|
|
@ -712,7 +659,7 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
cols_to_draw = []
|
||||
while len(users_to_draw) > 0:
|
||||
col = []
|
||||
for i in range(0, min(avatars_per_row, len(users_to_draw))):
|
||||
for _i in range(0, min(avatars_per_row, len(users_to_draw))):
|
||||
col.append(users_to_draw.pop(0))
|
||||
cols_to_draw.append(col)
|
||||
for col in cols_to_draw:
|
||||
|
|
@ -725,22 +672,22 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
largest_text_width = 0
|
||||
for user in col:
|
||||
if not user:
|
||||
if doTitle:
|
||||
if do_title:
|
||||
# Draw header
|
||||
text_width = self.draw_title(
|
||||
context, current_x, current_y, avatar_size, line_height)
|
||||
largest_text_width = max(
|
||||
text_width, largest_text_width)
|
||||
current_y += line_height + self.icon_spacing
|
||||
doTitle = False
|
||||
elif doConnection:
|
||||
do_title = False
|
||||
elif do_connection:
|
||||
# Draw header
|
||||
text_width = self.draw_connection(
|
||||
context, current_x, current_y, avatar_size, line_height)
|
||||
largest_text_width = max(
|
||||
text_width, largest_text_width)
|
||||
current_y += line_height + self.icon_spacing
|
||||
doConnection = False
|
||||
do_connection = False
|
||||
|
||||
else:
|
||||
text_width = self.draw_avatar(
|
||||
|
|
@ -758,9 +705,7 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
self.context = None
|
||||
|
||||
def recv_avatar(self, identifier, pix, mask):
|
||||
"""
|
||||
Called when image_getter has downloaded an image
|
||||
"""
|
||||
"""Called when image_getter has downloaded an image"""
|
||||
if identifier == 'def':
|
||||
self.def_avatar = pix
|
||||
self.def_avatar_mask = mask
|
||||
|
|
@ -773,16 +718,12 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
self.set_needs_redraw()
|
||||
|
||||
def delete_avatar(self, identifier):
|
||||
"""
|
||||
Remove avatar image
|
||||
"""
|
||||
"""Remove avatar image"""
|
||||
if identifier in self.avatars:
|
||||
del self.avatars[identifier]
|
||||
|
||||
def draw_title(self, context, pos_x, pos_y, avatar_size, line_height):
|
||||
"""
|
||||
Draw title at given Y position. Includes both text and image based on settings
|
||||
"""
|
||||
"""Draw title at given Y position. Includes both text and image based on settings"""
|
||||
tw = 0
|
||||
if not self.horizontal and not self.icon_only:
|
||||
title = self.channel_title
|
||||
|
|
@ -826,9 +767,7 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
_("VOICE_CONNECTED")
|
||||
|
||||
def draw_connection(self, context, pos_x, pos_y, avatar_size, line_height):
|
||||
"""
|
||||
Draw title at given Y position. Includes both text and image based on settings
|
||||
"""
|
||||
"""Draw title at given Y position. Includes both text and image based on settings"""
|
||||
tw = 0
|
||||
if not self.horizontal and not self.icon_only:
|
||||
tw = self.draw_text(
|
||||
|
|
@ -846,13 +785,11 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
return tw
|
||||
|
||||
def draw_avatar(self, context, user, pos_x, pos_y, avatar_size, line_height):
|
||||
"""
|
||||
Draw avatar at given Y position. Includes both text and image based on settings
|
||||
"""
|
||||
"""Draw avatar at given Y position. Includes both text and image based on settings"""
|
||||
# Ensure pixbuf for avatar
|
||||
if user["id"] not in self.avatars and user["avatar"] and avatar_size > 0:
|
||||
url = "https://cdn.discordapp.com/avatars/%s/%s.png" % (
|
||||
user['id'], user['avatar'])
|
||||
url = f"https://cdn.discordapp.com/avatars/{
|
||||
user['id']}/{user['avatar']}.png"
|
||||
get_surface(self.recv_avatar, url, user["id"],
|
||||
self.avatar_size)
|
||||
|
||||
|
|
@ -907,19 +844,18 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
self.mute_bg_col, avatar_size)
|
||||
return tw
|
||||
|
||||
def draw_text(self, context, string, pos_x, pos_y, tx_col, bg_col, avatar_size, line_height, font):
|
||||
"""
|
||||
Draw username & background at given position
|
||||
"""
|
||||
def draw_text(self, context, string, pos_x, pos_y,
|
||||
tx_col, bg_col, avatar_size, line_height, font):
|
||||
"""Draw username & background at given position"""
|
||||
if self.nick_length < 32 and len(string) > self.nick_length:
|
||||
string = string[:(self.nick_length-1)] + u"\u2026"
|
||||
string = string[:(self.nick_length-1)] + "\u2026"
|
||||
|
||||
context.save()
|
||||
layout = self.create_pango_layout(string)
|
||||
layout.set_auto_dir(True)
|
||||
layout.set_markup(string, -1)
|
||||
(floating_x, floating_y, floating_width,
|
||||
floating_height) = self.get_floating_coords()
|
||||
(_floating_x, _floating_y, floating_width,
|
||||
_floating_height) = self.get_floating_coords()
|
||||
layout.set_width(Pango.SCALE * floating_width)
|
||||
layout.set_spacing(Pango.SCALE * 3)
|
||||
if font:
|
||||
|
|
@ -971,6 +907,7 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
return text_width
|
||||
|
||||
def blank_avatar(self, context, pos_x, pos_y, avatar_size):
|
||||
"""Draw a cut-out of the previous shape with a forcible transparent hole"""
|
||||
context.save()
|
||||
if self.round_avatar:
|
||||
context.arc(pos_x + (avatar_size / 2), pos_y +
|
||||
|
|
@ -983,9 +920,7 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
context.restore()
|
||||
|
||||
def draw_avatar_pix(self, context, pixbuf, mask, pos_x, pos_y, border_colour, avatar_size):
|
||||
"""
|
||||
Draw avatar image at given position
|
||||
"""
|
||||
"""Draw avatar image at given position"""
|
||||
if not self.show_avatar:
|
||||
return
|
||||
# Empty the space for this
|
||||
|
|
@ -1021,13 +956,16 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
if self.round_avatar:
|
||||
context.new_path()
|
||||
context.arc(pos_x + (avatar_size / 2), pos_y +
|
||||
(avatar_size / 2), avatar_size / 2 + (self.border_width/2.0), 0, 2 * math.pi)
|
||||
(avatar_size / 2), avatar_size / 2 +
|
||||
(self.border_width/2.0), 0, 2 * math.pi)
|
||||
context.set_line_width(self.border_width)
|
||||
context.stroke()
|
||||
else:
|
||||
context.new_path()
|
||||
context.rectangle(pos_x - (self.border_width/2), pos_y - (self.border_width/2),
|
||||
avatar_size + self.border_width, avatar_size + self.border_width)
|
||||
context.rectangle(pos_x - (self.border_width/2),
|
||||
pos_y - (self.border_width/2),
|
||||
avatar_size + self.border_width,
|
||||
avatar_size + self.border_width)
|
||||
context.set_line_width(self.border_width)
|
||||
|
||||
context.stroke()
|
||||
|
|
@ -1053,13 +991,12 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
context.clip()
|
||||
context.set_operator(cairo.OPERATOR_OVER)
|
||||
draw_img_to_rect(pixbuf, context, pos_x, pos_y,
|
||||
avatar_size, avatar_size, False, False, 0, 0, self.fade_opacity * self.icon_transparency)
|
||||
avatar_size, avatar_size, False, False, 0, 0,
|
||||
self.fade_opacity * self.icon_transparency)
|
||||
context.restore()
|
||||
|
||||
def draw_mute(self, context, pos_x, pos_y, bg_col, avatar_size):
|
||||
"""
|
||||
Draw Mute logo
|
||||
"""
|
||||
"""Draw Mute logo"""
|
||||
if avatar_size <= 0:
|
||||
return
|
||||
context.save()
|
||||
|
|
@ -1126,9 +1063,7 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
context.restore()
|
||||
|
||||
def draw_deaf(self, context, pos_x, pos_y, bg_col, avatar_size):
|
||||
"""
|
||||
Draw deaf logo
|
||||
"""
|
||||
"""Draw deaf logo"""
|
||||
if avatar_size <= 0:
|
||||
return
|
||||
context.save()
|
||||
|
|
@ -1188,6 +1123,7 @@ class VoiceOverlayWindow(OverlayWindow):
|
|||
context.restore()
|
||||
|
||||
def draw_connection_icon(self, context, pos_x, pos_y, avatar_size):
|
||||
"""Draw a series of bars to show connectivity state"""
|
||||
context.save()
|
||||
context.translate(pos_x, pos_y)
|
||||
context.scale(avatar_size, avatar_size)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue