- Heavy refactor

- - Rename unused vars with _ or removed
- - Remove unused sections of code
- - Change name collisions with Gtk/bultins
- Bumped internal version to 0.3.0 in prep
This commit is contained in:
trigg 2020-10-19 17:46:13 +00:00
parent 060fc11818
commit 39b1a19e3c
16 changed files with 436 additions and 276 deletions

View file

@ -1 +1,14 @@
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""Entry point for Discover Overlay"""
from .discover_overlay import *

View file

@ -1,4 +1,16 @@
import sys
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""A class to assist auto-start"""
import os
import logging
try:
@ -9,6 +21,8 @@ except ModuleNotFoundError:
class Autostart:
"""A class to assist auto-start"""
def __init__(self, app_name):
if not app_name.endswith(".desktop"):
app_name = "%s.desktop" % (app_name)
@ -19,35 +33,37 @@ class Autostart:
xdg_data_home, 'applications/'), '/usr/share/applications/']
self.auto = self.find_auto()
self.desktop = self.find_desktop()
logging.info("Autostart info : desktop %s auto %s" %
(self.desktop, self.auto))
logging.info("Autostart info : desktop %s auto %s",
self.desktop, self.auto)
def find_auto(self):
for p in self.auto_locations:
file = os.path.join(p, self.app_name)
"""Check all known locations for auto-started apps"""
for path in self.auto_locations:
file = os.path.join(path, self.app_name)
if os.path.exists(file):
return file
return None
def find_desktop(self):
for p in self.desktop_locations:
file = os.path.join(p, self.app_name)
"""Check all known locations for desktop apps"""
for path in self.desktop_locations:
file = os.path.join(path, self.app_name)
if os.path.exists(file):
return file
return None
def set_autostart(self, b):
if b and not self.auto:
def set_autostart(self, enable):
"""Set or Unset auto-start state"""
if enable and not self.auto:
# Enable
d = os.path.join(xdg_config_home, 'autostart')
self.auto = os.path.join(d, self.app_name)
directory = os.path.join(xdg_config_home, 'autostart')
self.auto = os.path.join(directory, self.app_name)
os.symlink(self.desktop, self.auto)
pass
elif not b and self.auto:
elif not enable and self.auto:
# Disable
if os.path.islink(self.auto):
os.remove(self.auto)
pass
def is_auto(self):
"""Check if it's already set to auto-start"""
return True if self.auto else False

View file

@ -1,12 +1,23 @@
import websocket
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import select
import time
import json
import re
import sys
import requests
import logging
import calendar
import websocket
import requests
class DiscordConnector:
@ -17,7 +28,6 @@ class DiscordConnector:
self.voice_overlay = voice_overlay
self.ws = None
self.access_token = "none"
# TODO Magic number
self.oauth_token = "207646673902501888"
self.access_delay = 0
self.warn_connection = True
@ -47,7 +57,7 @@ class DiscordConnector:
x = requests.post(url, json=myobj)
try:
j = json.loads(x.text)
except:
except json.JSONDecodeError:
j = {}
if "access_token" in j:
self.access_token = j["access_token"]
@ -62,7 +72,7 @@ class DiscordConnector:
if channel != self.current_voice:
cn = self.channels[channel]['name']
logging.info(
"Joined room: %s" % (cn))
"Joined room: %s", cn)
self.sub_voice_channel(channel)
self.current_voice = channel
if need_req:
@ -75,7 +85,7 @@ class DiscordConnector:
if channel != self.current_text:
self.current_text = channel
logging.info(
"Changing text room: %s" % (channel))
"Changing text room: %s", channel)
if need_req:
self.req_channel_details(channel)
@ -203,8 +213,6 @@ class DiscordConnector:
if j["data"]["user"]["id"] == self.user["id"]:
self.in_room = []
# self.sub_all_voice()
else:
un = j["data"]["user"]["username"]
elif j["evt"] == "SPEAKING_START":
self.list_altered = True
# It's only possible to get alerts for the room you're in
@ -245,9 +253,9 @@ class DiscordConnector:
self.req_guilds()
self.user = j["data"]["user"]
logging.info(
"ID is %s" % (self.user["id"]))
"ID is %s", self.user["id"])
logging.info(
"Logged in as %s" % (self.user["username"]))
"Logged in as %s", self.user["username"])
self.authed = True
return
elif j["cmd"] == "GET_GUILDS":
@ -307,14 +315,14 @@ class DiscordConnector:
for channel in guild["channels"]:
channels = channels + " " + channel["name"]
logging.info(
u"%s: %s" % (guild["name"], channels))
u"%s: %s", guild["name"], channels)
self.sub_server()
self.find_user()
if self.last_text_channel:
self.sub_text_channel(self.last_text_channel)
def on_error(self, error):
logging.error("ERROR : %s" % (error))
logging.error("ERROR : %s", error)
def on_close(self):
logging.info("Connection closed")
@ -341,7 +349,7 @@ class DiscordConnector:
if self.channels[channel]["type"] == 2:
self.req_channel_details(channel)
count += 1
logging.warn("Getting %s rooms" %(count))
logging.warning("Getting %s rooms", count)
def sub_raw(self, cmd, channel, nonce):
self.ws.send("{\"cmd\":\"SUBSCRIBE\",\"args\":{%s},\"evt\":\"%s\",\"nonce\":\"%s\"}" % (
@ -424,14 +432,14 @@ class DiscordConnector:
self.set_text_channel(self.text_settings.get_channel())
# Poll socket for new information
r, w, e = select.select((self.ws.sock,), (), (), 0)
r, _w, _e = select.select((self.ws.sock,), (), (), 0)
while r:
try:
# Recieve & send to on_message
msg = self.ws.recv()
self.on_message(msg)
r, w, e = select.select((self.ws.sock,), (), (), 0)
except websocket._exceptions.WebSocketConnectionClosedException:
r, _w, _e = select.select((self.ws.sock,), (), (), 0)
except websocket.WebSocketConnectionClosedException:
self.on_close()
return True
return True
@ -448,8 +456,7 @@ class DiscordConnector:
try:
self.ws = websocket.create_connection("ws://127.0.0.1:6463/?v=1&client_id=%s" % (self.oauth_token),
origin="https://streamkit.discord.com")
except Exception as e:
except ConnectionError as e:
if self.error_connection:
logging.error(e)
self.error_connection = False
pass

View file

@ -10,18 +10,18 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import os
import sys
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GLib, Gio
import select
import logging
import pidfile
from .settings_window import MainSettingsWindow
from .voice_overlay import VoiceOverlayWindow
from .text_overlay import TextOverlayWindow
from .discord_connector import DiscordConnector
import logging
import pidfile
import os
import sys
gi.require_version("Gtk", "3.0")
# pylint: disable=wrong-import-position
from gi.repository import Gtk, GLib, Gio
try:
from xdg.BaseDirectory import xdg_config_home
@ -31,6 +31,9 @@ except ModuleNotFoundError:
class Discover:
def __init__(self, rpc_file, args):
self.ind = None
self.tray = None
self.create_gui()
self.connection = DiscordConnector(
self.settings.text_settings, self.settings.voice_settings,
@ -44,10 +47,7 @@ class Discover:
monitor.connect("changed", self.rpc_changed)
self.do_args(args)
try:
Gtk.main()
except:
pass
def do_args(self, data):
if "--help" in data:
@ -59,8 +59,7 @@ class Discover:
elif "--close" in data:
sys.exit(0)
def rpc_changed(self, a=None, b=None, c=None,d=None):
def rpc_changed(self, _a=None, _b=None, _c=None, _d=None):
with open(self.rpc_file, "r") as tfile:
data = tfile.readlines()
if len(data) >= 1:
@ -71,12 +70,14 @@ class Discover:
self.text_overlay = TextOverlayWindow(self)
self.menu = self.make_menu()
self.make_sys_tray_icon(self.menu)
self.settings = MainSettingsWindow(self.text_overlay, self.voice_overlay)
self.settings = MainSettingsWindow(
self.text_overlay, self.voice_overlay)
def make_sys_tray_icon(self, menu):
# Create AppIndicator
try:
gi.require_version('AppIndicator3', '0.1')
# pylint: disable=import-outside-toplevel
from gi.repository import AppIndicator3
self.ind = AppIndicator3.Indicator.new(
"discover_overlay",
@ -84,9 +85,9 @@ class Discover:
AppIndicator3.IndicatorCategory.APPLICATION_STATUS)
self.ind.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
self.ind.set_menu(menu)
except Exception as e:
except ImportError as exception:
# Create System Tray
logging.info("Falling back to Systray : %s" % (e))
logging.info("Falling back to Systray : %s", exception)
self.tray = Gtk.StatusIcon.new_from_icon_name("discover-overlay")
self.tray.connect('popup-menu', self.show_menu)
@ -109,10 +110,10 @@ class Discover:
self.menu.popup(
None, None, Gtk.StatusIcon.position_menu, obj, button, time)
def show_settings(self, obj=None, data=None):
self.settings.present()
def show_settings(self, _obj=None, _data=None):
self.settings.present_settings()
def close(self, a=None, b=None, c=None):
def close(self, _a=None, _b=None, _c=None):
Gtk.main_quit()
@ -129,9 +130,8 @@ def entrypoint():
logging.getLogger().setLevel(logging.INFO)
Discover(rpc_file, line)
except pidfile.AlreadyRunningError:
logging.warn("Discover overlay is currently running")
logging.warning("Discover overlay is currently running")
with open(rpc_file, "w") as tfile:
tfile.write(line)
logging.warn("Sent RPC command")
logging.warning("Sent RPC command")

View file

@ -1,11 +1,26 @@
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""An X11 window which can be moved and resized"""
import gi
gi.require_version("Gtk", "3.0")
import cairo
gi.require_version("Gtk", "3.0")
# pylint: disable=wrong-import-position
from gi.repository import Gtk, Gdk
import logging
class DraggableWindow(Gtk.Window):
"""An X11 window which can be moved and resized"""
def __init__(self, x=0, y=0, w=300, h=300, message="Message", settings=None):
Gtk.Window.__init__(self, type=Gtk.WindowType.POPUP)
if w < 100:
@ -20,7 +35,7 @@ class DraggableWindow(Gtk.Window):
self.message = message
self.set_size_request(50, 50)
self.connect('draw', self.draw)
self.connect('draw', self.dodraw)
self.connect('motion-notify-event', self.drag)
self.connect('button-press-event', self.button_press)
self.connect('button-release-event', self.button_release)
@ -45,28 +60,15 @@ class DraggableWindow(Gtk.Window):
self.show_all()
def force_location(self):
"""Move the window to previously given co-ords. Also double check sanity on layer & decorations"""
self.set_decorated(False)
self.set_keep_above(True)
display = Gdk.Display.get_default()
if "get_monitor" in dir(display):
monitor = display.get_monitor(self.monitor)
geometry = monitor.get_geometry()
scale_factor = monitor.get_scale_factor()
w = scale_factor * geometry.width
h = scale_factor * geometry.height
x = geometry.x
y = geometry.y
else:
screen = display.get_default_screen()
w = screen.width()
h = screen.height()
x = 0
y = 0
#self.resize(400, h)
self.move(self.x, self.y)
self.resize(self.w, self.h)
def drag(self, w, event):
def drag(self, _w, event):
"""Called by GTK while mouse is moving over window. Used to resize and move"""
if event.state & Gdk.ModifierType.BUTTON1_MASK:
if self.drag_type == 1:
# Center is move
@ -92,6 +94,7 @@ class DraggableWindow(Gtk.Window):
self.force_location()
def button_press(self, w, event):
"""Called when a mouse button is pressed on this window"""
(w, h) = self.get_size()
if not self.drag_type:
self.drag_type = 1
@ -103,10 +106,12 @@ class DraggableWindow(Gtk.Window):
self.drag_x = event.x
self.drag_y = event.y
def button_release(self, w, event):
def button_release(self, _w, _event):
"""Called when a mouse button is released"""
self.drag_type = None
def draw(self, widget, context):
def dodraw(self, _widget, context):
"""Draw our window."""
context.set_source_rgba(1.0, 1.0, 0.0, 0.7)
# Don't layer drawing over each other, always replace
context.set_operator(cairo.OPERATOR_SOURCE)
@ -117,7 +122,7 @@ class DraggableWindow(Gtk.Window):
# Draw text
context.set_source_rgba(0.0, 0.0, 0.0, 1.0)
xb, yb, w, h, dx, dy = context.text_extents(self.message)
_xb, _yb, w, h, _dx, _dy = context.text_extents(self.message)
context.move_to(sw / 2 - w / 2, sh / 2 - h / 2)
context.show_text(self.message)
@ -130,6 +135,7 @@ class DraggableWindow(Gtk.Window):
context.fill()
def get_coords(self):
(x, y) = self.placement_window.get_position()
(w, h) = self.placement_window.get_size()
"""Return window position and size"""
(x, y) = self.get_position()
(w, h) = self.get_size()
return (x, y, w, h)

View file

@ -1,11 +1,26 @@
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""A Wayland full-screen window which can be moved and resized"""
import cairo
import gi
gi.require_version("Gtk", "3.0")
import cairo
# pylint: disable=wrong-import-position
from gi.repository import Gtk, Gdk, GtkLayerShell
import logging
class DraggableWindowWayland(Gtk.Window):
"""A Wayland full-screen window which can be moved and resized"""
def __init__(self, x=0, y=0, w=300, h=300, message="Message", settings=None):
Gtk.Window.__init__(self, type=Gtk.WindowType.TOPLEVEL)
if w < 100:
@ -20,7 +35,7 @@ class DraggableWindowWayland(Gtk.Window):
self.message = message
self.set_size_request(50, 50)
self.connect('draw', self.draw)
self.connect('draw', self.dodraw)
self.connect('motion-notify-event', self.drag)
self.connect('button-press-event', self.button_press)
self.connect('button-release-event', self.button_release)
@ -42,18 +57,20 @@ class DraggableWindowWayland(Gtk.Window):
# self.force_location()
def force_location(self):
(sx, sy)= self.get_size()
"""Move the window to previously given co-ords. In wayland just clip to current screen"""
(size_x, size_y) = self.get_size()
if self.x < 0:
self.x = 0
if self.y < 0:
self.y = 0
if self.x + self.w > sx:
self.x = sx - self.w
if self.y + self.h > sy:
self.y = sy - self.h
if self.x + self.w > size_x:
self.x = size_x - self.w
if self.y + self.h > size_y:
self.y = size_y - self.h
self.queue_draw()
def drag(self, w, event):
def drag(self, _w, event):
"""Called by GTK while mouse is moving over window. Used to resize and move"""
if event.state & Gdk.ModifierType.BUTTON1_MASK:
if self.drag_type == 1:
# Center is move
@ -81,7 +98,8 @@ class DraggableWindowWayland(Gtk.Window):
self.drag_y = event.y
self.force_location()
def button_press(self, w, event):
def button_press(self, _w, event):
"""Called when a mouse button is pressed on this window"""
px = event.x - self.x
py = event.y - self.y
@ -97,10 +115,12 @@ class DraggableWindowWayland(Gtk.Window):
self.drag_x = event.x
self.drag_y = event.y
def button_release(self, w, event):
def button_release(self, _w, _event):
"""Called when a mouse button is released"""
self.drag_type = None
def draw(self, widget, context):
def dodraw(self, _widget, context):
"""Draw our window. For wayland we're secretly a fullscreen app and need to draw only a single rectangle of the overlay"""
context.translate(self.x, self.y)
context.save()
context.rectangle(0, 0, self.w, self.h)
@ -114,8 +134,8 @@ class DraggableWindowWayland(Gtk.Window):
# Draw text
context.set_source_rgba(0.0, 0.0, 0.0, 1.0)
xb, yb, w, h, dx, dy = context.text_extents(self.message)
context.move_to(self.w / 2 - w / 2, self.h / 2 - h / 2)
_xb, _yb, width, height, _dx, _dy = context.text_extents(self.message)
context.move_to(self.w / 2 - width / 2, self.h / 2 - height / 2)
context.show_text(self.message)
# Draw resizing edges
@ -132,6 +152,6 @@ class DraggableWindowWayland(Gtk.Window):
context.fill()
context.restore()
def get_coords(self):
"""Return the position and size of the window"""
return (self.x, self.y, self.w, self.h)

View file

@ -1,19 +1,31 @@
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import gi
gi.require_version("Gtk", "3.0")
import json
from configparser import ConfigParser
from .draggable_window import DraggableWindow
from .settings import SettingsWindow
from .autostart import Autostart
from gi.repository import Gtk, Gdk, Pango
import logging
gi.require_version("Gtk", "3.0")
# pylint: disable=wrong-import-position
from gi.repository import Gtk
class GeneralSettingsWindow(SettingsWindow):
def __init__(self, overlay, overlay2):
Gtk.VBox.__init__(self)
SettingsWindow.__init__(self)
self.overlay = overlay
self.overlay2 = overlay
self.overlay2 = overlay2
self.xshape = None
self.autostart = None
self.set_size_request(400, 200)
self.connect("destroy", self.close_window)
self.connect("delete-event", self.close_window)

View file

@ -1,21 +1,32 @@
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import gi
gi.require_version("Gtk", "3.0")
gi.require_version('GdkPixbuf', '2.0')
import urllib
import requests
import threading
from gi.repository.GdkPixbuf import Pixbuf
from gi.repository import Gtk, Gio, GdkPixbuf, Gdk
import cairo
import logging
import PIL.Image as Image
from io import BytesIO
gi.require_version("Gtk", "3.0")
gi.require_version('GdkPixbuf', '2.0')
# pylint: disable=wrong-import-position
from gi.repository import Gio, GdkPixbuf
class Image_Getter():
def __init__(self, func, url, id, size):
def __init__(self, func, url, identifier, size):
self.func = func
self.id = id
self.id = identifier
self.url = url
self.size = size
@ -28,36 +39,22 @@ class Image_Getter():
response = urllib.request.urlopen(req)
input_stream = Gio.MemoryInputStream.new_from_data(
response.read(), None)
pixbuf = Pixbuf.new_from_stream(input_stream, None)
pixbuf = GdkPixbuf.Pixbuf.new_from_stream(input_stream, None)
if self.size:
pixbuf = pixbuf.scale_simple(self.size, self.size,
GdkPixbuf.InterpType.BILINEAR)
# elif self.limit_x or self.limit_y:
# px = pixbuf.width()
# py = pixbuf.height()
# aspect = px / py
# scale = 1.0
# if self.limit_x and self.limit_y:
# scale = min(self.limit_x / px, self.limit_y / py, 1.0)
# elif self.limit_x:
# scale = min(self.limit_x / px, 1.0)
# elif self.limit_y:
# scale = min(self.limit_y / py, 1.0)##
#
# pixbuf = pixbuf.scale_simple(int(px * scale), int(py * scale),
# GdkPixbuf.InterpType.BILINEAR)
self.func(self.id, pixbuf)
except Exception as e:
except urllib.error.URLError as exception:
logging.error(
"Could not access : %s" % (self.url))
logging.error(e)
"Could not access : %s", self.url)
logging.error(exception)
class Surface_Getter():
def __init__(self, func, url, id, size):
def __init__(self, func, url, identifier, size):
self.func = func
self.id = id
self.id = identifier
self.url = url
self.size = size
@ -71,33 +68,41 @@ class Surface_Getter():
surf = self.from_pil(im)
self.func(self.id, surf)
except:
logging.error("Unable to open %s" % (self.url))
except requests.HTTPError:
logging.error("Unable to open %s", self.url)
except requests.TooManyRedirects:
logging.error("Unable to open %s - Too many redirects", self.url)
except requests.Timeout:
logging.error("Unable to open %s - Timeout", self.url)
except requests.ConnectionError:
logging.error("Unable to open %s - Connection error", self.url)
except ValueError:
logging.error("Unable to read %s", self.url)
except TypeError:
logging.error("Unable to read %s", self.url)
def from_pil(self, im, alpha=1.0, format=cairo.FORMAT_ARGB32):
def from_pil(self, im, alpha=1.0):
"""
:param im: Pillow Image
:param alpha: 0..1 alpha to add to non-alpha images
:param format: Pixel format for output surface
"""
assert format in (
cairo.FORMAT_RGB24, cairo.FORMAT_ARGB32), "Unsupported pixel format: %s" % format
if 'A' not in im.getbands():
im.putalpha(int(alpha * 256.))
arr = bytearray(im.tobytes('raw', 'BGRa'))
surface = cairo.ImageSurface.create_for_data(
arr, format, im.width, im.height)
arr, cairo.FORMAT_ARGB32, im.width, im.height)
return surface
def get_image(func, id, ava, size):
image_getter = Image_Getter(func, id, ava, size)
def get_image(func, identifier, ava, size):
image_getter = Image_Getter(func, identifier, ava, size)
t = threading.Thread(target=image_getter.get_url, args=())
t.start()
def get_surface(func, id, ava, size):
image_getter = Surface_Getter(func, id, ava, size)
def get_surface(func, identifier, ava, size):
image_getter = Surface_Getter(func, identifier, ava, size)
t = threading.Thread(target=image_getter.get_url, args=())
t.start()
@ -106,7 +111,7 @@ def get_aspected_size(img, w, h, anchor=0, hanchor=0):
px = img.get_width()
py = img.get_height()
if py < 1 or h < 1:
return (0, 0)
return (0, 0, 0, 0)
img_aspect = px / py
rect_aspect = w / h

View file

@ -1,9 +1,22 @@
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gdk, GtkLayerShell
import cairo
import logging
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import sys
import logging
import gi
import cairo
gi.require_version("Gtk", "3.0")
# pylint: disable=wrong-import-position
from gi.repository import Gtk, Gdk, GtkLayerShell
class OverlayWindow(Gtk.Window):
@ -23,7 +36,7 @@ class OverlayWindow(Gtk.Window):
screen = self.get_screen()
self.set_size_request(50, 50)
self.connect('draw', self.draw)
self.connect('draw', self.overlay_draw)
self.compositing = False
# Set RGBA
@ -65,10 +78,7 @@ class OverlayWindow(Gtk.Window):
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.BOTTOM, True)
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.TOP, True)
def draw(self, widget, context):
self.do_draw(context)
def do_draw(self, context):
def overlay_draw(self, _w, context, data=None):
pass
def set_font(self, name, size):
@ -146,7 +156,7 @@ class OverlayWindow(Gtk.Window):
(w, h) = self.get_size()
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h)
surface_ctx = cairo.Context(surface)
self.do_draw(surface_ctx)
self.overlay_draw(None, surface_ctx)
reg = Gdk.cairo_region_create_from_surface(surface)
gdkwin.shape_combine_region(reg, 0, 0)
else:
@ -156,7 +166,6 @@ class OverlayWindow(Gtk.Window):
def set_monitor(self, idx=None, mon=None):
self.monitor = idx
if self.is_wayland:
print(self)
if mon:
GtkLayerShell.set_monitor(self, mon)
self.force_location()

View file

@ -1,9 +1,21 @@
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import os
import logging
import gi
gi.require_version("Gtk", "3.0")
import sys
import os
# pylint: disable=wrong-import-position
from gi.repository import Gtk, Gdk
import logging
try:
@ -13,13 +25,24 @@ except ModuleNotFoundError:
class SettingsWindow(Gtk.VBox):
def __init__(self):
Gtk.VBox.__init__(self)
self.placement_window = None
self.configDir = None
self.configFile = None
self.overlay = None
self.floating_x = None
self.floating_y = None
self.floating_w = None
self.floating_h = None
def init_config(self):
self.configDir = os.path.join(xdg_config_home, "discover_overlay")
os.makedirs(self.configDir, exist_ok=True)
self.configFile = os.path.join(self.configDir, "config.ini")
self.read_config()
def close_window(self, a=None, b=None):
def close_window(self, _a=None, _b=None):
if self.placement_window:
(x, y) = self.placement_window.get_position()
(w, h) = self.placement_window.get_size()
@ -41,7 +64,7 @@ class SettingsWindow(Gtk.VBox):
if display.get_monitor(i).get_model() == name:
return i
logging.info(
"Could not find monitor : %s" % (name))
"Could not find monitor : %s", name)
return 0
def get_monitor_obj(self, name):
@ -51,8 +74,14 @@ class SettingsWindow(Gtk.VBox):
if display.get_monitor(i).get_model() == name:
return display.get_monitor(i)
logging.info(
"Could not find monitor : %s" % (name))
"Could not find monitor : %s", name)
return None
def present(self):
def present_settings(self):
self.show_all()
def read_config(self):
pass
def save_config(self):
pass

View file

@ -1,13 +1,22 @@
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from .voice_settings import VoiceSettingsWindow
from .text_settings import TextSettingsWindow
from .general_settings import GeneralSettingsWindow
import gi
gi.require_version("Gtk", "3.0")
import sys
import os
from gi.repository import Gtk, Gdk
import logging
# pylint: disable=wrong-import-position
from gi.repository import Gtk
class MainSettingsWindow(Gtk.Window):
@ -33,7 +42,8 @@ class MainSettingsWindow(Gtk.Window):
self.text_settings = TextSettingsWindow(self.text_overlay)
nb.append_page(self.text_settings)
nb.set_tab_label_text(self.text_settings, "Text")
self.core_settings = GeneralSettingsWindow(self.text_overlay,self.voice_overlay)
self.core_settings = GeneralSettingsWindow(
self.text_overlay, self.voice_overlay)
nb.append_page(self.core_settings)
nb.set_tab_label_text(self.core_settings, "Core")
self.add(nb)
@ -46,9 +56,9 @@ class MainSettingsWindow(Gtk.Window):
self.hide()
return True
def present(self):
self.voice_settings.present()
self.text_settings.present()
self.core_settings.present()
def present_settings(self):
self.voice_settings.present_settings()
self.text_settings.present_settings()
self.core_settings.present_settings()
self.nb.show()
self.show()

View file

@ -1,14 +1,27 @@
import gi
gi.require_version("Gtk", "3.0")
gi.require_version('PangoCairo', '1.0')
import math
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from .overlay import OverlayWindow
from gi.repository import Gtk, Gdk, Pango, PangoCairo
import cairo
import logging
import time
import re
from .image_getter import get_surface, draw_img_to_rect, get_aspected_size
import gi
gi.require_version("Gtk", "3.0")
gi.require_version('PangoCairo', '1.0')
# pylint: disable=wrong-import-position
from gi.repository import Pango, PangoCairo
class TextOverlayWindow(OverlayWindow):
@ -111,17 +124,14 @@ class TextOverlayWindow(OverlayWindow):
elif msg['type'] == 'br':
ret = '\n'
else:
logging.error("Unknown text type : %s" % (msg["type"]))
logging.error("Unknown text type : %s", msg["type"])
return ret
def recv_attach(self, id, pix):
self.attachment[id] = pix
def recv_attach(self, identifier, pix):
self.attachment[identifier] = pix
self.redraw()
def draw(self, widget, ctx):
self.do_draw(ctx)
def do_draw(self, context):
def overlay_draw(self, _w, context, data=None):
self.context = context
context.set_antialias(cairo.ANTIALIAS_GOOD)
(w, h) = self.get_size()
@ -141,8 +151,6 @@ class TextOverlayWindow(OverlayWindow):
context.rectangle(0, 0, w, h)
context.clip()
cy = h
tnow = time.time()
for line in reversed(self.content):
@ -181,13 +189,13 @@ class TextOverlayWindow(OverlayWindow):
ih = pix.get_height()
iw = min(iw, self.w)
ih = min(ih, (self.h * .7))
(ax, ay, aw, ah) = get_aspected_size(pix, iw, ih)
(_ax, _ay, _aw, ah) = get_aspected_size(pix, iw, ih)
self.col(self.bg_col)
self.context.rectangle(0, y - ah, self.w, ah)
self.context.fill()
self.context.set_operator(cairo.OPERATOR_OVER)
new_w, new_h = draw_img_to_rect(
_new_w, new_h = draw_img_to_rect(
pix, self.context, 0, y - ih, iw, ih, aspect=True)
return y - new_h
return y
@ -204,7 +212,7 @@ class TextOverlayWindow(OverlayWindow):
font = Pango.FontDescription(
"%s %s" % (self.text_font, self.text_size))
layout.set_font_description(font)
tw, th = layout.get_pixel_size()
_tw, th = layout.get_pixel_size()
self.col(self.bg_col)
self.context.rectangle(0, y - th, self.w, th)
self.context.fill()
@ -223,7 +231,7 @@ class TextOverlayWindow(OverlayWindow):
if len(self.imgList) <= count:
break # We fucked up. Who types ` anyway
url = self.imgList[count]
#url = self.imgList[count]
at = Pango.attr_shape_new_with_data(
self.pango_rect, self.pango_rect, count, None)
@ -236,7 +244,7 @@ class TextOverlayWindow(OverlayWindow):
PangoCairo.show_layout(self.context, layout)
return y - th
def render_custom(self, ctx, shape, path, data):
def render_custom(self, ctx, shape, path, _data):
key = self.imgList[shape.data]['url']
if key not in self.attachment:
get_surface(self.recv_attach,

View file

@ -1,18 +1,33 @@
import gi
gi.require_version("Gtk", "3.0")
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import json
from configparser import ConfigParser
from .draggable_window import DraggableWindow
from .draggable_window_wayland import DraggableWindowWayland
from .settings import SettingsWindow
from gi.repository import Gtk, Gdk, Pango
import logging
import gi
gi.require_version("Gtk", "3.0")
# pylint: disable=wrong-import-position
from gi.repository import Gtk, Gdk, Pango
GUILD_DEFAULT_VALUE = "0"
class TextSettingsWindow(SettingsWindow):
def __init__(self, overlay):
Gtk.VBox.__init__(self)
SettingsWindow.__init__(self)
self.overlay = overlay
self.set_size_request(400, 200)
self.connect("destroy", self.close_window)
@ -27,7 +42,6 @@ class TextSettingsWindow(SettingsWindow):
self.ignore_guild_change = False
self.create_gui()
def update_channel_model(self):
# potentially organize channels by their group/parent_id
# https://discord.com/developers/docs/resources/channel#channel-object-channel-structure
@ -65,13 +79,12 @@ class TextSettingsWindow(SettingsWindow):
break
idx += 1
def add_connector(self, conn):
self.connector = conn
if self.channel:
self.connector.start_listening_text(self.channel)
def present(self):
def present_settings(self):
self.show_all()
if not self.floating:
self.align_x_widget.show()
@ -164,13 +177,14 @@ class TextSettingsWindow(SettingsWindow):
"text", "show_attach", fallback=True)
logging.info(
"Loading saved channel %s" % (self.channel))
"Loading saved channel %s", self.channel)
# Pass all of our config over to the overlay
self.overlay.set_enabled(self.enabled)
self.overlay.set_align_x(self.align_x)
self.overlay.set_align_y(self.align_y)
self.overlay.set_monitor(self.get_monitor_index(self.monitor),self.get_monitor_obj(self.monitor))
self.overlay.set_monitor(self.get_monitor_index(
self.monitor), self.get_monitor_obj(self.monitor))
self.overlay.set_floating(
self.floating, self.floating_x, self.floating_y, self.floating_w, self.floating_h)
self.overlay.set_bg(self.bg_col)
@ -306,7 +320,6 @@ class TextSettingsWindow(SettingsWindow):
channel.connect("changed", self.change_channel)
rt = Gtk.CellRendererText()
#channel.set_row_separator_func(lambda model, path: model[path][1])
channel.pack_start(rt, True)
channel.add_attribute(rt, "text", 0)
channel.add_attribute(rt, 'sensitive', 1)
@ -315,8 +328,7 @@ class TextSettingsWindow(SettingsWindow):
guild = Gtk.ComboBox.new()
guild.connect("changed", self.change_guild)
guild_rt = Gtk.CellRendererText()
#guild.set_row_separator_func(lambda model, path: model[path][1])
rt = Gtk.CellRendererText()
guild.pack_start(rt, True)
guild.add_attribute(rt, "text", 0)
guild.add_attribute(rt, 'sensitive', 1)
@ -384,7 +396,6 @@ class TextSettingsWindow(SettingsWindow):
self.channel = c
self.save_config()
def change_guild(self, button):
if self.ignore_guild_change:
return

View file

@ -1,12 +1,19 @@
import gi
gi.require_version("Gtk", "3.0")
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import math
import cairo
from .overlay import OverlayWindow
from .image_getter import get_surface, draw_img_to_rect
from gi.repository import Gtk, Gdk
import cairo
import logging
class VoiceOverlayWindow(OverlayWindow):
@ -137,11 +144,7 @@ class VoiceOverlayWindow(OverlayWindow):
self.connected = is_connected
self.redraw()
def draw(self, widget, context):
# Draw
self.do_draw(context)
def do_draw(self, context):
def overlay_draw(self, _w, context, _data=None):
self.context = context
context.set_antialias(cairo.ANTIALIAS_GOOD)
# Get size of window
@ -163,7 +166,6 @@ class VoiceOverlayWindow(OverlayWindow):
context.rectangle(0, 0, w, h)
context.clip()
context.set_operator(cairo.OPERATOR_OVER)
if not self.connected:
return
@ -219,16 +221,16 @@ class VoiceOverlayWindow(OverlayWindow):
context.restore()
self.context = None
def recv_avatar(self, id, pix):
if id == 'def':
def recv_avatar(self, identifier, pix):
if identifier == 'def':
self.def_avatar = pix
else:
self.avatars[id] = pix
self.avatars[identifier] = pix
self.redraw()
def delete_avatar(self, id):
def delete_avatar(self, identifier):
if id in self.avatars:
del self.avatars[id]
del self.avatars[identifier]
def draw_avatar(self, context, user, y):
# Ensure pixbuf for avatar
@ -282,7 +284,7 @@ class VoiceOverlayWindow(OverlayWindow):
context.set_font_face(cairo.ToyFontFace(
self.text_font, cairo.FontSlant.NORMAL, cairo.FontWeight.NORMAL))
context.set_font_size(self.text_size)
xb, yb, w, h, dx, dy = context.text_extents(string)
_xb, _yb, w, h, _dx, _dy = context.text_extents(string)
ho = (self.avatar_size / 2) - (h / 2)
if self.align_right:
context.move_to(0, 0)

View file

@ -1,27 +1,41 @@
import gi
gi.require_version("Gtk", "3.0")
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import json
from configparser import ConfigParser
import gi
from .draggable_window import DraggableWindow
from .draggable_window_wayland import DraggableWindowWayland
from .settings import SettingsWindow
gi.require_version("Gtk", "3.0")
# pylint: disable=wrong-import-position
from gi.repository import Gtk, Gdk, Pango
import logging
class VoiceSettingsWindow(SettingsWindow):
def __init__(self, overlay):
Gtk.VBox.__init__(self)
SettingsWindow.__init__(self)
self.overlay = overlay
self.set_size_request(400, 200)
self.connect("destroy", self.close_window)
self.connect("delete-event", self.close_window)
self.placement_window = None
self.align_x = None
self.align_y = None
self.init_config()
self.create_gui()
def present(self):
def present_settings(self):
self.show_all()
if not self.floating:
self.align_x_widget.show()
@ -85,7 +99,8 @@ class VoiceSettingsWindow(SettingsWindow):
self.overlay.set_only_speaking(self.only_speaking)
self.overlay.set_highlight_self(self.highlight_self)
self.overlay.set_icon_only(self.icon_only)
self.overlay.set_monitor(self.get_monitor_index(self.monitor),self.get_monitor_obj(self.monitor))
self.overlay.set_monitor(self.get_monitor_index(
self.monitor), self.get_monitor_obj(self.monitor))
self.overlay.set_vert_edge_padding(self.vert_edge_padding)
self.overlay.set_horz_edge_padding(self.horz_edge_padding)
self.overlay.set_order(self.order)
@ -337,8 +352,6 @@ class VoiceSettingsWindow(SettingsWindow):
self.add(box)
pass
def change_placement(self, button):
if self.placement_window:
(x, y, w, h) = self.placement_window.get_coords()
@ -347,7 +360,7 @@ class VoiceSettingsWindow(SettingsWindow):
self.floating_w = w
self.floating_h = h
self.overlay.set_floating(True, x, y, w, h)
self.save_config
self.save_config()
if not self.overlay.is_wayland:
button.set_label("Place Window")
self.placement_window.close()
@ -448,7 +461,6 @@ class VoiceSettingsWindow(SettingsWindow):
m_s = mon.get_model()
self.overlay.set_monitor(button.get_active(), mon)
self.monitor = m_s
self.save_config()

View file

@ -9,7 +9,7 @@ setup(
name='discover-overlay',
author='trigg',
author_email='',
version='0.2.2',
version='0.3.0',
description='Voice chat overlay',
long_description=readme(),
long_description_content_type='text/markdown',