- 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 * 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 os
import logging import logging
try: try:
@ -9,6 +21,8 @@ except ModuleNotFoundError:
class Autostart: class Autostart:
"""A class to assist auto-start"""
def __init__(self, app_name): def __init__(self, app_name):
if not app_name.endswith(".desktop"): if not app_name.endswith(".desktop"):
app_name = "%s.desktop" % (app_name) app_name = "%s.desktop" % (app_name)
@ -19,35 +33,37 @@ class Autostart:
xdg_data_home, 'applications/'), '/usr/share/applications/'] xdg_data_home, 'applications/'), '/usr/share/applications/']
self.auto = self.find_auto() self.auto = self.find_auto()
self.desktop = self.find_desktop() self.desktop = self.find_desktop()
logging.info("Autostart info : desktop %s auto %s" % logging.info("Autostart info : desktop %s auto %s",
(self.desktop, self.auto)) self.desktop, self.auto)
def find_auto(self): def find_auto(self):
for p in self.auto_locations: """Check all known locations for auto-started apps"""
file = os.path.join(p, self.app_name) for path in self.auto_locations:
file = os.path.join(path, self.app_name)
if os.path.exists(file): if os.path.exists(file):
return file return file
return None return None
def find_desktop(self): def find_desktop(self):
for p in self.desktop_locations: """Check all known locations for desktop apps"""
file = os.path.join(p, self.app_name) for path in self.desktop_locations:
file = os.path.join(path, self.app_name)
if os.path.exists(file): if os.path.exists(file):
return file return file
return None return None
def set_autostart(self, b): def set_autostart(self, enable):
if b and not self.auto: """Set or Unset auto-start state"""
if enable and not self.auto:
# Enable # Enable
d = os.path.join(xdg_config_home, 'autostart') directory = os.path.join(xdg_config_home, 'autostart')
self.auto = os.path.join(d, self.app_name) self.auto = os.path.join(directory, self.app_name)
os.symlink(self.desktop, self.auto) os.symlink(self.desktop, self.auto)
pass elif not enable and self.auto:
elif not b and self.auto:
# Disable # Disable
if os.path.islink(self.auto): if os.path.islink(self.auto):
os.remove(self.auto) os.remove(self.auto)
pass
def is_auto(self): def is_auto(self):
"""Check if it's already set to auto-start"""
return True if self.auto else False 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 select
import time import time
import json import json
import re
import sys import sys
import requests
import logging import logging
import calendar import calendar
import websocket
import requests
class DiscordConnector: class DiscordConnector:
@ -17,7 +28,6 @@ class DiscordConnector:
self.voice_overlay = voice_overlay self.voice_overlay = voice_overlay
self.ws = None self.ws = None
self.access_token = "none" self.access_token = "none"
# TODO Magic number
self.oauth_token = "207646673902501888" self.oauth_token = "207646673902501888"
self.access_delay = 0 self.access_delay = 0
self.warn_connection = True self.warn_connection = True
@ -47,7 +57,7 @@ class DiscordConnector:
x = requests.post(url, json=myobj) x = requests.post(url, json=myobj)
try: try:
j = json.loads(x.text) j = json.loads(x.text)
except: except json.JSONDecodeError:
j = {} j = {}
if "access_token" in j: if "access_token" in j:
self.access_token = j["access_token"] self.access_token = j["access_token"]
@ -62,7 +72,7 @@ class DiscordConnector:
if channel != self.current_voice: if channel != self.current_voice:
cn = self.channels[channel]['name'] cn = self.channels[channel]['name']
logging.info( logging.info(
"Joined room: %s" % (cn)) "Joined room: %s", cn)
self.sub_voice_channel(channel) self.sub_voice_channel(channel)
self.current_voice = channel self.current_voice = channel
if need_req: if need_req:
@ -75,7 +85,7 @@ class DiscordConnector:
if channel != self.current_text: if channel != self.current_text:
self.current_text = channel self.current_text = channel
logging.info( logging.info(
"Changing text room: %s" % (channel)) "Changing text room: %s", channel)
if need_req: if need_req:
self.req_channel_details(channel) self.req_channel_details(channel)
@ -202,9 +212,7 @@ class DiscordConnector:
self.set_in_room(j["data"]["user"]["id"], False) self.set_in_room(j["data"]["user"]["id"], False)
if j["data"]["user"]["id"] == self.user["id"]: if j["data"]["user"]["id"] == self.user["id"]:
self.in_room = [] self.in_room = []
#self.sub_all_voice() # self.sub_all_voice()
else:
un = j["data"]["user"]["username"]
elif j["evt"] == "SPEAKING_START": elif j["evt"] == "SPEAKING_START":
self.list_altered = True self.list_altered = True
# It's only possible to get alerts for the room you're in # It's only possible to get alerts for the room you're in
@ -245,9 +253,9 @@ class DiscordConnector:
self.req_guilds() self.req_guilds()
self.user = j["data"]["user"] self.user = j["data"]["user"]
logging.info( logging.info(
"ID is %s" % (self.user["id"])) "ID is %s", self.user["id"])
logging.info( logging.info(
"Logged in as %s" % (self.user["username"])) "Logged in as %s", self.user["username"])
self.authed = True self.authed = True
return return
elif j["cmd"] == "GET_GUILDS": elif j["cmd"] == "GET_GUILDS":
@ -264,8 +272,8 @@ class DiscordConnector:
if channel["type"] == 2: if channel["type"] == 2:
self.req_channel_details(channel["id"]) self.req_channel_details(channel["id"])
self.check_guilds() self.check_guilds()
#self.sub_all_voice_guild(j["nonce"]) # self.sub_all_voice_guild(j["nonce"])
#self.sub_all_text_guild(j["nonce"]) # self.sub_all_text_guild(j["nonce"])
return return
elif j["cmd"] == "SUBSCRIBE": elif j["cmd"] == "SUBSCRIBE":
return return
@ -307,14 +315,14 @@ class DiscordConnector:
for channel in guild["channels"]: for channel in guild["channels"]:
channels = channels + " " + channel["name"] channels = channels + " " + channel["name"]
logging.info( logging.info(
u"%s: %s" % (guild["name"], channels)) u"%s: %s", guild["name"], channels)
self.sub_server() self.sub_server()
self.find_user() self.find_user()
if self.last_text_channel: if self.last_text_channel:
self.sub_text_channel(self.last_text_channel) self.sub_text_channel(self.last_text_channel)
def on_error(self, error): def on_error(self, error):
logging.error("ERROR : %s" % (error)) logging.error("ERROR : %s", error)
def on_close(self): def on_close(self):
logging.info("Connection closed") logging.info("Connection closed")
@ -340,8 +348,8 @@ class DiscordConnector:
for channel in self.channels: for channel in self.channels:
if self.channels[channel]["type"] == 2: if self.channels[channel]["type"] == 2:
self.req_channel_details(channel) self.req_channel_details(channel)
count+=1 count += 1
logging.warn("Getting %s rooms" %(count)) logging.warning("Getting %s rooms", count)
def sub_raw(self, cmd, channel, nonce): def sub_raw(self, cmd, channel, nonce):
self.ws.send("{\"cmd\":\"SUBSCRIBE\",\"args\":{%s},\"evt\":\"%s\",\"nonce\":\"%s\"}" % ( 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()) self.set_text_channel(self.text_settings.get_channel())
# Poll socket for new information # 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: while r:
try: try:
# Recieve & send to on_message # Recieve & send to on_message
msg = self.ws.recv() msg = self.ws.recv()
self.on_message(msg) self.on_message(msg)
r, w, e = select.select((self.ws.sock,), (), (), 0) r, _w, _e = select.select((self.ws.sock,), (), (), 0)
except websocket._exceptions.WebSocketConnectionClosedException: except websocket.WebSocketConnectionClosedException:
self.on_close() self.on_close()
return True return True
return True return True
@ -448,8 +456,7 @@ class DiscordConnector:
try: try:
self.ws = websocket.create_connection("ws://127.0.0.1:6463/?v=1&client_id=%s" % (self.oauth_token), self.ws = websocket.create_connection("ws://127.0.0.1:6463/?v=1&client_id=%s" % (self.oauth_token),
origin="https://streamkit.discord.com") origin="https://streamkit.discord.com")
except Exception as e: except ConnectionError as e:
if self.error_connection: if self.error_connection:
logging.error(e) logging.error(e)
self.error_connection = False self.error_connection = False
pass

View file

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

View file

@ -1,12 +1,27 @@
# 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 import gi
gi.require_version("Gtk", "3.0")
import cairo import cairo
gi.require_version("Gtk", "3.0")
# pylint: disable=wrong-import-position
from gi.repository import Gtk, Gdk from gi.repository import Gtk, Gdk
import logging
class DraggableWindow(Gtk.Window): class DraggableWindow(Gtk.Window):
def __init__(self, x=0, y=0, w=300, h=300, message="Message",settings=None): """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) Gtk.Window.__init__(self, type=Gtk.WindowType.POPUP)
if w < 100: if w < 100:
w = 100 w = 100
@ -16,11 +31,11 @@ class DraggableWindow(Gtk.Window):
self.y = y self.y = y
self.w = w self.w = w
self.h = h self.h = h
self.settings=settings self.settings = settings
self.message = message self.message = message
self.set_size_request(50, 50) 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('motion-notify-event', self.drag)
self.connect('button-press-event', self.button_press) self.connect('button-press-event', self.button_press)
self.connect('button-release-event', self.button_release) self.connect('button-release-event', self.button_release)
@ -45,28 +60,15 @@ class DraggableWindow(Gtk.Window):
self.show_all() self.show_all()
def force_location(self): 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_decorated(False)
self.set_keep_above(True) 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.move(self.x, self.y)
self.resize(self.w, self.h) 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 event.state & Gdk.ModifierType.BUTTON1_MASK:
if self.drag_type == 1: if self.drag_type == 1:
# Center is move # Center is move
@ -92,6 +94,7 @@ class DraggableWindow(Gtk.Window):
self.force_location() 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"""
(w, h) = self.get_size() (w, h) = self.get_size()
if not self.drag_type: if not self.drag_type:
self.drag_type = 1 self.drag_type = 1
@ -103,10 +106,12 @@ class DraggableWindow(Gtk.Window):
self.drag_x = event.x self.drag_x = event.x
self.drag_y = event.y 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 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) context.set_source_rgba(1.0, 1.0, 0.0, 0.7)
# Don't layer drawing over each other, always replace # Don't layer drawing over each other, always replace
context.set_operator(cairo.OPERATOR_SOURCE) context.set_operator(cairo.OPERATOR_SOURCE)
@ -117,7 +122,7 @@ class DraggableWindow(Gtk.Window):
# Draw text # Draw text
context.set_source_rgba(0.0, 0.0, 0.0, 1.0) 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.move_to(sw / 2 - w / 2, sh / 2 - h / 2)
context.show_text(self.message) context.show_text(self.message)
@ -130,6 +135,7 @@ class DraggableWindow(Gtk.Window):
context.fill() context.fill()
def get_coords(self): def get_coords(self):
(x, y) = self.placement_window.get_position() """Return window position and size"""
(w, h) = self.placement_window.get_size() (x, y) = self.get_position()
return (x,y,w,h) (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 import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
import cairo # pylint: disable=wrong-import-position
from gi.repository import Gtk, Gdk, GtkLayerShell from gi.repository import Gtk, Gdk, GtkLayerShell
import logging
class DraggableWindowWayland(Gtk.Window): 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): def __init__(self, x=0, y=0, w=300, h=300, message="Message", settings=None):
Gtk.Window.__init__(self, type=Gtk.WindowType.TOPLEVEL) Gtk.Window.__init__(self, type=Gtk.WindowType.TOPLEVEL)
if w < 100: if w < 100:
@ -16,11 +31,11 @@ class DraggableWindowWayland(Gtk.Window):
self.y = y self.y = y
self.w = w self.w = w
self.h = h self.h = h
self.settings=settings self.settings = settings
self.message = message self.message = message
self.set_size_request(50, 50) 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('motion-notify-event', self.drag)
self.connect('button-press-event', self.button_press) self.connect('button-press-event', self.button_press)
self.connect('button-release-event', self.button_release) self.connect('button-release-event', self.button_release)
@ -39,21 +54,23 @@ class DraggableWindowWayland(Gtk.Window):
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.TOP, True) GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.TOP, True)
self.show_all() self.show_all()
#self.force_location() # self.force_location()
def force_location(self): 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: if self.x < 0:
self.x=0 self.x = 0
if self.y < 0: if self.y < 0:
self.y=0 self.y = 0
if self.x + self.w > sx: if self.x + self.w > size_x:
self.x = sx - self.w self.x = size_x - self.w
if self.y + self.h > sy: if self.y + self.h > size_y:
self.y = sy - self.h self.y = size_y - self.h
self.queue_draw() 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 event.state & Gdk.ModifierType.BUTTON1_MASK:
if self.drag_type == 1: if self.drag_type == 1:
# Center is move # Center is move
@ -81,14 +98,15 @@ class DraggableWindowWayland(Gtk.Window):
self.drag_y = event.y self.drag_y = event.y
self.force_location() 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 px = event.x - self.x
py = event.y - self.y py = event.y - self.y
if not self.drag_type: if not self.drag_type:
self.drag_type = 1 self.drag_type = 1
# Where in the window did we press? # Where in the window did we press?
if px < 20 and py<20: if px < 20 and py < 20:
self.settings.change_placement(None) self.settings.change_placement(None)
if py > self.h - 32: if py > self.h - 32:
self.drag_type += 2 self.drag_type += 2
@ -97,13 +115,15 @@ class DraggableWindowWayland(Gtk.Window):
self.drag_x = event.x self.drag_x = event.x
self.drag_y = event.y 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 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.translate(self.x, self.y)
context.save() context.save()
context.rectangle(0,0,self.w,self.h) context.rectangle(0, 0, self.w, self.h)
context.clip() context.clip()
context.set_source_rgba(1.0, 1.0, 0.0, 0.7) context.set_source_rgba(1.0, 1.0, 0.0, 0.7)
@ -114,8 +134,8 @@ class DraggableWindowWayland(Gtk.Window):
# Draw text # Draw text
context.set_source_rgba(0.0, 0.0, 0.0, 1.0) 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, width, height, _dx, _dy = context.text_extents(self.message)
context.move_to(self.w / 2 - w / 2, self.h / 2 - h / 2) context.move_to(self.w / 2 - width / 2, self.h / 2 - height / 2)
context.show_text(self.message) context.show_text(self.message)
# Draw resizing edges # Draw resizing edges
@ -128,10 +148,10 @@ class DraggableWindowWayland(Gtk.Window):
# Draw Done! # Draw Done!
context.set_source_rgba(0.0, 1.0, 0.0, 0.5) context.set_source_rgba(0.0, 1.0, 0.0, 0.5)
context.rectangle(0, 0, 20,20) context.rectangle(0, 0, 20, 20)
context.fill() context.fill()
context.restore() context.restore()
def get_coords(self): def get_coords(self):
return (self.x,self.y,self.w,self.h) """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 import gi
gi.require_version("Gtk", "3.0")
import json
from configparser import ConfigParser from configparser import ConfigParser
from .draggable_window import DraggableWindow
from .settings import SettingsWindow from .settings import SettingsWindow
from .autostart import Autostart from .autostart import Autostart
from gi.repository import Gtk, Gdk, Pango gi.require_version("Gtk", "3.0")
import logging # pylint: disable=wrong-import-position
from gi.repository import Gtk
class GeneralSettingsWindow(SettingsWindow): class GeneralSettingsWindow(SettingsWindow):
def __init__(self, overlay, overlay2): def __init__(self, overlay, overlay2):
Gtk.VBox.__init__(self) SettingsWindow.__init__(self)
self.overlay = overlay self.overlay = overlay
self.overlay2 = overlay self.overlay2 = overlay2
self.xshape = None
self.autostart = None
self.set_size_request(400, 200) self.set_size_request(400, 200)
self.connect("destroy", self.close_window) self.connect("destroy", self.close_window)
self.connect("delete-event", self.close_window) self.connect("delete-event", self.close_window)
@ -73,4 +85,4 @@ class GeneralSettingsWindow(SettingsWindow):
self.overlay.set_force_xshape(button.get_active()) self.overlay.set_force_xshape(button.get_active())
self.overlay2.set_force_xshape(button.get_active()) self.overlay2.set_force_xshape(button.get_active())
self.xshape = button.get_active() self.xshape = button.get_active()
self.save_config() self.save_config()

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 import gi
gi.require_version("Gtk", "3.0")
gi.require_version('GdkPixbuf', '2.0')
import urllib import urllib
import requests import requests
import threading import threading
from gi.repository.GdkPixbuf import Pixbuf
from gi.repository import Gtk, Gio, GdkPixbuf, Gdk
import cairo import cairo
import logging import logging
import PIL.Image as Image 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(): class Image_Getter():
def __init__(self, func, url, id, size): def __init__(self, func, url, identifier, size):
self.func = func self.func = func
self.id = id self.id = identifier
self.url = url self.url = url
self.size = size self.size = size
@ -28,36 +39,22 @@ class Image_Getter():
response = urllib.request.urlopen(req) response = urllib.request.urlopen(req)
input_stream = Gio.MemoryInputStream.new_from_data( input_stream = Gio.MemoryInputStream.new_from_data(
response.read(), None) response.read(), None)
pixbuf = Pixbuf.new_from_stream(input_stream, None) pixbuf = GdkPixbuf.Pixbuf.new_from_stream(input_stream, None)
if self.size: if self.size:
pixbuf = pixbuf.scale_simple(self.size, self.size, pixbuf = pixbuf.scale_simple(self.size, self.size,
GdkPixbuf.InterpType.BILINEAR) 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) self.func(self.id, pixbuf)
except Exception as e: except urllib.error.URLError as exception:
logging.error( logging.error(
"Could not access : %s" % (self.url)) "Could not access : %s", self.url)
logging.error(e) logging.error(exception)
class Surface_Getter(): class Surface_Getter():
def __init__(self, func, url, id, size): def __init__(self, func, url, identifier, size):
self.func = func self.func = func
self.id = id self.id = identifier
self.url = url self.url = url
self.size = size self.size = size
@ -71,33 +68,41 @@ class Surface_Getter():
surf = self.from_pil(im) surf = self.from_pil(im)
self.func(self.id, surf) self.func(self.id, surf)
except: except requests.HTTPError:
logging.error("Unable to open %s" % (self.url)) 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 im: Pillow Image
:param alpha: 0..1 alpha to add to non-alpha images :param alpha: 0..1 alpha to add to non-alpha images
:param format: Pixel format for output surface :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(): if 'A' not in im.getbands():
im.putalpha(int(alpha * 256.)) im.putalpha(int(alpha * 256.))
arr = bytearray(im.tobytes('raw', 'BGRa')) arr = bytearray(im.tobytes('raw', 'BGRa'))
surface = cairo.ImageSurface.create_for_data( surface = cairo.ImageSurface.create_for_data(
arr, format, im.width, im.height) arr, cairo.FORMAT_ARGB32, im.width, im.height)
return surface return surface
def get_image(func, id, ava, size): def get_image(func, identifier, ava, size):
image_getter = Image_Getter(func, id, ava, size) image_getter = Image_Getter(func, identifier, ava, size)
t = threading.Thread(target=image_getter.get_url, args=()) t = threading.Thread(target=image_getter.get_url, args=())
t.start() t.start()
def get_surface(func, id, ava, size): def get_surface(func, identifier, ava, size):
image_getter = Surface_Getter(func, id, ava, size) image_getter = Surface_Getter(func, identifier, ava, size)
t = threading.Thread(target=image_getter.get_url, args=()) t = threading.Thread(target=image_getter.get_url, args=())
t.start() t.start()
@ -106,7 +111,7 @@ def get_aspected_size(img, w, h, anchor=0, hanchor=0):
px = img.get_width() px = img.get_width()
py = img.get_height() py = img.get_height()
if py < 1 or h < 1: if py < 1 or h < 1:
return (0, 0) return (0, 0, 0, 0)
img_aspect = px / py img_aspect = px / py
rect_aspect = w / h rect_aspect = w / h

View file

@ -1,9 +1,22 @@
import gi # This program is free software: you can redistribute it and/or modify
gi.require_version("Gtk", "3.0") # it under the terms of the GNU General Public License as published by
from gi.repository import Gtk, Gdk, GtkLayerShell # the Free Software Foundation, either version 3 of the License, or
import cairo # (at your option) any later version.
import logging #
# 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 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): class OverlayWindow(Gtk.Window):
@ -11,9 +24,9 @@ class OverlayWindow(Gtk.Window):
window = Gtk.Window() window = Gtk.Window()
screen = window.get_screen() screen = window.get_screen()
screen_type = "%s" % (screen) screen_type = "%s" % (screen)
self.is_wayland=False self.is_wayland = False
if "Wayland" in screen_type: if "Wayland" in screen_type:
self.is_wayland=True self.is_wayland = True
print("Using Wayland GDK. Expect bugs") print("Using Wayland GDK. Expect bugs")
return Gtk.WindowType.TOPLEVEL return Gtk.WindowType.TOPLEVEL
return Gtk.WindowType.POPUP return Gtk.WindowType.POPUP
@ -23,7 +36,7 @@ class OverlayWindow(Gtk.Window):
screen = self.get_screen() screen = self.get_screen()
self.set_size_request(50, 50) self.set_size_request(50, 50)
self.connect('draw', self.draw) self.connect('draw', self.overlay_draw)
self.compositing = False self.compositing = False
# Set RGBA # Set RGBA
@ -53,8 +66,8 @@ class OverlayWindow(Gtk.Window):
self.align_right = True self.align_right = True
self.align_vert = 1 self.align_vert = 1
self.floating = False self.floating = False
self.force_xshape= False self.force_xshape = False
self.context=None self.context = None
def set_wayland_state(self): def set_wayland_state(self):
if self.is_wayland: if self.is_wayland:
@ -65,10 +78,7 @@ class OverlayWindow(Gtk.Window):
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.BOTTOM, True) GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.BOTTOM, True)
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.TOP, True) GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.TOP, True)
def draw(self, widget, context): def overlay_draw(self, _w, context, data=None):
self.do_draw(context)
def do_draw(self, context):
pass pass
def set_font(self, name, size): def set_font(self, name, size):
@ -129,7 +139,7 @@ class OverlayWindow(Gtk.Window):
self.resize(self.w, self.h) self.resize(self.w, self.h)
if not self.floating: if not self.floating:
(w,h) = self.get_size() (w, h) = self.get_size()
self.w = w self.w = w
self.h = h self.h = h
self.redraw() self.redraw()
@ -137,7 +147,7 @@ class OverlayWindow(Gtk.Window):
def redraw(self): def redraw(self):
gdkwin = self.get_window() gdkwin = self.get_window()
if not self.floating: if not self.floating:
(w,h) = self.get_size() (w, h) = self.get_size()
self.w = w self.w = w
self.h = h self.h = h
@ -146,7 +156,7 @@ class OverlayWindow(Gtk.Window):
(w, h) = self.get_size() (w, h) = self.get_size()
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h) surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h)
surface_ctx = cairo.Context(surface) surface_ctx = cairo.Context(surface)
self.do_draw(surface_ctx) self.overlay_draw(None, surface_ctx)
reg = Gdk.cairo_region_create_from_surface(surface) reg = Gdk.cairo_region_create_from_surface(surface)
gdkwin.shape_combine_region(reg, 0, 0) gdkwin.shape_combine_region(reg, 0, 0)
else: else:
@ -156,7 +166,6 @@ class OverlayWindow(Gtk.Window):
def set_monitor(self, idx=None, mon=None): def set_monitor(self, idx=None, mon=None):
self.monitor = idx self.monitor = idx
if self.is_wayland: if self.is_wayland:
print(self)
if mon: if mon:
GtkLayerShell.set_monitor(self, mon) GtkLayerShell.set_monitor(self, mon)
self.force_location() 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 import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
import sys # pylint: disable=wrong-import-position
import os
from gi.repository import Gtk, Gdk from gi.repository import Gtk, Gdk
import logging
try: try:
@ -13,13 +25,24 @@ except ModuleNotFoundError:
class SettingsWindow(Gtk.VBox): 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): def init_config(self):
self.configDir = os.path.join(xdg_config_home, "discover_overlay") self.configDir = os.path.join(xdg_config_home, "discover_overlay")
os.makedirs(self.configDir, exist_ok=True) os.makedirs(self.configDir, exist_ok=True)
self.configFile = os.path.join(self.configDir, "config.ini") self.configFile = os.path.join(self.configDir, "config.ini")
self.read_config() self.read_config()
def close_window(self, a=None, b=None): def close_window(self, _a=None, _b=None):
if self.placement_window: if self.placement_window:
(x, y) = self.placement_window.get_position() (x, y) = self.placement_window.get_position()
(w, h) = self.placement_window.get_size() (w, h) = self.placement_window.get_size()
@ -41,7 +64,7 @@ class SettingsWindow(Gtk.VBox):
if display.get_monitor(i).get_model() == name: if display.get_monitor(i).get_model() == name:
return i return i
logging.info( logging.info(
"Could not find monitor : %s" % (name)) "Could not find monitor : %s", name)
return 0 return 0
def get_monitor_obj(self, name): def get_monitor_obj(self, name):
@ -51,8 +74,14 @@ class SettingsWindow(Gtk.VBox):
if display.get_monitor(i).get_model() == name: if display.get_monitor(i).get_model() == name:
return display.get_monitor(i) return display.get_monitor(i)
logging.info( logging.info(
"Could not find monitor : %s" % (name)) "Could not find monitor : %s", name)
return None return None
def present(self): def present_settings(self):
self.show_all() 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 .voice_settings import VoiceSettingsWindow
from .text_settings import TextSettingsWindow from .text_settings import TextSettingsWindow
from .general_settings import GeneralSettingsWindow from .general_settings import GeneralSettingsWindow
import gi import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
import sys # pylint: disable=wrong-import-position
import os from gi.repository import Gtk
from gi.repository import Gtk, Gdk
import logging
class MainSettingsWindow(Gtk.Window): class MainSettingsWindow(Gtk.Window):
@ -25,7 +34,7 @@ class MainSettingsWindow(Gtk.Window):
# Create # Create
nb = Gtk.Notebook() nb = Gtk.Notebook()
#nb.set_tab_pos(Gtk.POS_TOP) # nb.set_tab_pos(Gtk.POS_TOP)
self.voice_settings = VoiceSettingsWindow(self.voice_overlay) self.voice_settings = VoiceSettingsWindow(self.voice_overlay)
nb.append_page(self.voice_settings) nb.append_page(self.voice_settings)
@ -33,22 +42,23 @@ class MainSettingsWindow(Gtk.Window):
self.text_settings = TextSettingsWindow(self.text_overlay) self.text_settings = TextSettingsWindow(self.text_overlay)
nb.append_page(self.text_settings) nb.append_page(self.text_settings)
nb.set_tab_label_text(self.text_settings, "Text") 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.append_page(self.core_settings)
nb.set_tab_label_text(self.core_settings, "Core") nb.set_tab_label_text(self.core_settings, "Core")
self.add(nb) self.add(nb)
self.nb=nb self.nb = nb
def close_window(self,a=None,b=None): def close_window(self, a=None, b=None):
self.text_settings.close_window(a,b) self.text_settings.close_window(a, b)
self.voice_settings.close_window(a,b) self.voice_settings.close_window(a, b)
self.core_settings.close_window(a,b) self.core_settings.close_window(a, b)
self.hide() self.hide()
return True return True
def present(self): def present_settings(self):
self.voice_settings.present() self.voice_settings.present_settings()
self.text_settings.present() self.text_settings.present_settings()
self.core_settings.present() self.core_settings.present_settings()
self.nb.show() self.nb.show()
self.show() self.show()

View file

@ -1,14 +1,27 @@
import gi # This program is free software: you can redistribute it and/or modify
gi.require_version("Gtk", "3.0") # it under the terms of the GNU General Public License as published by
gi.require_version('PangoCairo', '1.0') # the Free Software Foundation, either version 3 of the License, or
import math # (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 .overlay import OverlayWindow
from gi.repository import Gtk, Gdk, Pango, PangoCairo
import cairo import cairo
import logging import logging
import time import time
import re import re
from .image_getter import get_surface, draw_img_to_rect, get_aspected_size 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): class TextOverlayWindow(OverlayWindow):
@ -111,17 +124,14 @@ class TextOverlayWindow(OverlayWindow):
elif msg['type'] == 'br': elif msg['type'] == 'br':
ret = '\n' ret = '\n'
else: else:
logging.error("Unknown text type : %s" % (msg["type"])) logging.error("Unknown text type : %s", msg["type"])
return ret return ret
def recv_attach(self, id, pix): def recv_attach(self, identifier, pix):
self.attachment[id] = pix self.attachment[identifier] = pix
self.redraw() self.redraw()
def draw(self, widget, ctx): def overlay_draw(self, _w, context, data=None):
self.do_draw(ctx)
def do_draw(self, context):
self.context = context self.context = context
context.set_antialias(cairo.ANTIALIAS_GOOD) context.set_antialias(cairo.ANTIALIAS_GOOD)
(w, h) = self.get_size() (w, h) = self.get_size()
@ -138,10 +148,8 @@ class TextOverlayWindow(OverlayWindow):
w = self.w w = self.w
h = self.h h = self.h
context.translate(self.x, self.y) context.translate(self.x, self.y)
context.rectangle(0,0,w,h) context.rectangle(0, 0, w, h)
context.clip() context.clip()
cy = h cy = h
tnow = time.time() tnow = time.time()
@ -181,13 +189,13 @@ class TextOverlayWindow(OverlayWindow):
ih = pix.get_height() ih = pix.get_height()
iw = min(iw, self.w) iw = min(iw, self.w)
ih = min(ih, (self.h * .7)) 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.col(self.bg_col)
self.context.rectangle(0, y - ah, self.w, ah) self.context.rectangle(0, y - ah, self.w, ah)
self.context.fill() self.context.fill()
self.context.set_operator(cairo.OPERATOR_OVER) 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) pix, self.context, 0, y - ih, iw, ih, aspect=True)
return y - new_h return y - new_h
return y return y
@ -204,7 +212,7 @@ class TextOverlayWindow(OverlayWindow):
font = Pango.FontDescription( font = Pango.FontDescription(
"%s %s" % (self.text_font, self.text_size)) "%s %s" % (self.text_font, self.text_size))
layout.set_font_description(font) layout.set_font_description(font)
tw, th = layout.get_pixel_size() _tw, th = layout.get_pixel_size()
self.col(self.bg_col) self.col(self.bg_col)
self.context.rectangle(0, y - th, self.w, th) self.context.rectangle(0, y - th, self.w, th)
self.context.fill() self.context.fill()
@ -223,7 +231,7 @@ class TextOverlayWindow(OverlayWindow):
if len(self.imgList) <= count: if len(self.imgList) <= count:
break # We fucked up. Who types ` anyway break # We fucked up. Who types ` anyway
url = self.imgList[count] #url = self.imgList[count]
at = Pango.attr_shape_new_with_data( at = Pango.attr_shape_new_with_data(
self.pango_rect, self.pango_rect, count, None) self.pango_rect, self.pango_rect, count, None)
@ -236,7 +244,7 @@ class TextOverlayWindow(OverlayWindow):
PangoCairo.show_layout(self.context, layout) PangoCairo.show_layout(self.context, layout)
return y - th 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'] key = self.imgList[shape.data]['url']
if key not in self.attachment: if key not in self.attachment:
get_surface(self.recv_attach, get_surface(self.recv_attach,

View file

@ -1,18 +1,33 @@
import gi # This program is free software: you can redistribute it and/or modify
gi.require_version("Gtk", "3.0") # 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 import json
from configparser import ConfigParser from configparser import ConfigParser
from .draggable_window import DraggableWindow from .draggable_window import DraggableWindow
from .draggable_window_wayland import DraggableWindowWayland from .draggable_window_wayland import DraggableWindowWayland
from .settings import SettingsWindow from .settings import SettingsWindow
from gi.repository import Gtk, Gdk, Pango
import logging 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" GUILD_DEFAULT_VALUE = "0"
class TextSettingsWindow(SettingsWindow): class TextSettingsWindow(SettingsWindow):
def __init__(self, overlay): def __init__(self, overlay):
Gtk.VBox.__init__(self) SettingsWindow.__init__(self)
self.overlay = overlay self.overlay = overlay
self.set_size_request(400, 200) self.set_size_request(400, 200)
self.connect("destroy", self.close_window) self.connect("destroy", self.close_window)
@ -27,7 +42,6 @@ class TextSettingsWindow(SettingsWindow):
self.ignore_guild_change = False self.ignore_guild_change = False
self.create_gui() self.create_gui()
def update_channel_model(self): def update_channel_model(self):
# potentially organize channels by their group/parent_id # potentially organize channels by their group/parent_id
# https://discord.com/developers/docs/resources/channel#channel-object-channel-structure # https://discord.com/developers/docs/resources/channel#channel-object-channel-structure
@ -44,7 +58,7 @@ class TextSettingsWindow(SettingsWindow):
if chan['guild_id'] == guild_id: if chan['guild_id'] == guild_id:
c_model.append([chan["name"], True]) c_model.append([chan["name"], True])
self.channel_lookup.append(c) self.channel_lookup.append(c)
# if a guild is specified, poulate channel list with every channel from *just that guild* # if a guild is specified, poulate channel list with every channel from *just that guild*
if self.guild != GUILD_DEFAULT_VALUE: if self.guild != GUILD_DEFAULT_VALUE:
for c in self.list_channels_keys: for c in self.list_channels_keys:
@ -65,13 +79,12 @@ class TextSettingsWindow(SettingsWindow):
break break
idx += 1 idx += 1
def add_connector(self, conn): def add_connector(self, conn):
self.connector = conn self.connector = conn
if self.channel: if self.channel:
self.connector.start_listening_text(self.channel) self.connector.start_listening_text(self.channel)
def present(self): def present_settings(self):
self.show_all() self.show_all()
if not self.floating: if not self.floating:
self.align_x_widget.show() self.align_x_widget.show()
@ -130,7 +143,7 @@ class TextSettingsWindow(SettingsWindow):
if in_list[key]["type"] == 0: if in_list[key]["type"] == 0:
self.list_channels_keys.append(key) self.list_channels_keys.append(key)
self.list_channels_keys.sort() self.list_channels_keys.sort()
def set_guilds(self, in_list): def set_guilds(self, in_list):
self.list_guilds = in_list self.list_guilds = in_list
self.list_guilds_keys = [] self.list_guilds_keys = []
@ -164,13 +177,14 @@ class TextSettingsWindow(SettingsWindow):
"text", "show_attach", fallback=True) "text", "show_attach", fallback=True)
logging.info( logging.info(
"Loading saved channel %s" % (self.channel)) "Loading saved channel %s", self.channel)
# Pass all of our config over to the overlay # Pass all of our config over to the overlay
self.overlay.set_enabled(self.enabled) self.overlay.set_enabled(self.enabled)
self.overlay.set_align_x(self.align_x) self.overlay.set_align_x(self.align_x)
self.overlay.set_align_y(self.align_y) 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.overlay.set_floating(
self.floating, self.floating_x, self.floating_y, self.floating_w, self.floating_h) self.floating, self.floating_x, self.floating_y, self.floating_w, self.floating_h)
self.overlay.set_bg(self.bg_col) self.overlay.set_bg(self.bg_col)
@ -306,7 +320,6 @@ class TextSettingsWindow(SettingsWindow):
channel.connect("changed", self.change_channel) channel.connect("changed", self.change_channel)
rt = Gtk.CellRendererText() rt = Gtk.CellRendererText()
#channel.set_row_separator_func(lambda model, path: model[path][1])
channel.pack_start(rt, True) channel.pack_start(rt, True)
channel.add_attribute(rt, "text", 0) channel.add_attribute(rt, "text", 0)
channel.add_attribute(rt, 'sensitive', 1) channel.add_attribute(rt, 'sensitive', 1)
@ -315,8 +328,7 @@ class TextSettingsWindow(SettingsWindow):
guild = Gtk.ComboBox.new() guild = Gtk.ComboBox.new()
guild.connect("changed", self.change_guild) guild.connect("changed", self.change_guild)
guild_rt = Gtk.CellRendererText() rt = Gtk.CellRendererText()
#guild.set_row_separator_func(lambda model, path: model[path][1])
guild.pack_start(rt, True) guild.pack_start(rt, True)
guild.add_attribute(rt, "text", 0) guild.add_attribute(rt, "text", 0)
guild.add_attribute(rt, 'sensitive', 1) guild.add_attribute(rt, 'sensitive', 1)
@ -378,24 +390,23 @@ class TextSettingsWindow(SettingsWindow):
def change_channel(self, button): def change_channel(self, button):
if self.ignore_channel_change: if self.ignore_channel_change:
return return
c = self.channel_lookup[button.get_active()] c = self.channel_lookup[button.get_active()]
self.connector.start_listening_text(c) self.connector.start_listening_text(c)
self.channel = c self.channel = c
self.save_config() self.save_config()
def change_guild(self, button): def change_guild(self, button):
if self.ignore_guild_change: if self.ignore_guild_change:
return return
guild_id = self.guild_lookup[button.get_active()] guild_id = self.guild_lookup[button.get_active()]
self.guild = guild_id self.guild = guild_id
self.save_config() self.save_config()
self.update_channel_model() self.update_channel_model()
def change_placement(self, button): def change_placement(self, button):
if self.placement_window: if self.placement_window:
(x,y,w,h) = self.placement_window.get_coords() (x, y, w, h) = self.placement_window.get_coords()
self.floating_x = x self.floating_x = x
self.floating_y = y self.floating_y = y
self.floating_w = w self.floating_w = w
@ -412,12 +423,12 @@ class TextSettingsWindow(SettingsWindow):
self.placement_window = DraggableWindowWayland( self.placement_window = DraggableWindowWayland(
x=self.floating_x, y=self.floating_y, x=self.floating_x, y=self.floating_y,
w=self.floating_w, h=self.floating_h, w=self.floating_w, h=self.floating_h,
message="Place & resize this window then press Green!",settings=self) message="Place & resize this window then press Green!", settings=self)
else: else:
self.placement_window = DraggableWindow( self.placement_window = DraggableWindow(
x=self.floating_x, y=self.floating_y, x=self.floating_x, y=self.floating_y,
w=self.floating_w, h=self.floating_h, w=self.floating_w, h=self.floating_h,
message="Place & resize this window then press Save!",settings=self) message="Place & resize this window then press Save!", settings=self)
if not self.overlay.is_wayland: if not self.overlay.is_wayland:
button.set_label("Save this position") button.set_label("Save this position")

View file

@ -1,12 +1,19 @@
import gi # 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
gi.require_version("Gtk", "3.0") # 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 math
import cairo
from .overlay import OverlayWindow from .overlay import OverlayWindow
from .image_getter import get_surface, draw_img_to_rect from .image_getter import get_surface, draw_img_to_rect
from gi.repository import Gtk, Gdk
import cairo
import logging
class VoiceOverlayWindow(OverlayWindow): class VoiceOverlayWindow(OverlayWindow):
@ -37,8 +44,8 @@ class VoiceOverlayWindow(OverlayWindow):
self.connected = False self.connected = False
self.force_location() self.force_location()
get_surface(self.recv_avatar, get_surface(self.recv_avatar,
"https://cdn.discordapp.com/embed/avatars/3.png", "https://cdn.discordapp.com/embed/avatars/3.png",
'def', self.avatar_size) 'def', self.avatar_size)
self.set_title("Discover Voice") self.set_title("Discover Voice")
def set_bg(self, bg): def set_bg(self, bg):
@ -137,11 +144,7 @@ class VoiceOverlayWindow(OverlayWindow):
self.connected = is_connected self.connected = is_connected
self.redraw() self.redraw()
def draw(self, widget, context): def overlay_draw(self, _w, context, _data=None):
# Draw
self.do_draw(context)
def do_draw(self, context):
self.context = context self.context = context
context.set_antialias(cairo.ANTIALIAS_GOOD) context.set_antialias(cairo.ANTIALIAS_GOOD)
# Get size of window # Get size of window
@ -160,10 +163,9 @@ class VoiceOverlayWindow(OverlayWindow):
w = self.w w = self.w
h = self.h h = self.h
context.translate(self.x, self.y) context.translate(self.x, self.y)
context.rectangle(0,0,w,h) context.rectangle(0, 0, w, h)
context.clip() context.clip()
context.set_operator(cairo.OPERATOR_OVER) context.set_operator(cairo.OPERATOR_OVER)
if not self.connected: if not self.connected:
return return
@ -219,16 +221,16 @@ class VoiceOverlayWindow(OverlayWindow):
context.restore() context.restore()
self.context = None self.context = None
def recv_avatar(self, id, pix): def recv_avatar(self, identifier, pix):
if id == 'def': if identifier == 'def':
self.def_avatar = pix self.def_avatar = pix
else: else:
self.avatars[id] = pix self.avatars[identifier] = pix
self.redraw() self.redraw()
def delete_avatar(self, id): def delete_avatar(self, identifier):
if id in self.avatars: if id in self.avatars:
del self.avatars[id] del self.avatars[identifier]
def draw_avatar(self, context, user, y): def draw_avatar(self, context, user, y):
# Ensure pixbuf for avatar # Ensure pixbuf for avatar
@ -282,7 +284,7 @@ class VoiceOverlayWindow(OverlayWindow):
context.set_font_face(cairo.ToyFontFace( context.set_font_face(cairo.ToyFontFace(
self.text_font, cairo.FontSlant.NORMAL, cairo.FontWeight.NORMAL)) self.text_font, cairo.FontSlant.NORMAL, cairo.FontWeight.NORMAL))
context.set_font_size(self.text_size) 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) ho = (self.avatar_size / 2) - (h / 2)
if self.align_right: if self.align_right:
context.move_to(0, 0) context.move_to(0, 0)

View file

@ -1,27 +1,41 @@
import gi # This program is free software: you can redistribute it and/or modify
gi.require_version("Gtk", "3.0") # 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 import json
from configparser import ConfigParser from configparser import ConfigParser
import gi
from .draggable_window import DraggableWindow from .draggable_window import DraggableWindow
from .draggable_window_wayland import DraggableWindowWayland from .draggable_window_wayland import DraggableWindowWayland
from .settings import SettingsWindow from .settings import SettingsWindow
gi.require_version("Gtk", "3.0")
# pylint: disable=wrong-import-position
from gi.repository import Gtk, Gdk, Pango from gi.repository import Gtk, Gdk, Pango
import logging
class VoiceSettingsWindow(SettingsWindow): class VoiceSettingsWindow(SettingsWindow):
def __init__(self, overlay): def __init__(self, overlay):
Gtk.VBox.__init__(self) SettingsWindow.__init__(self)
self.overlay = overlay self.overlay = overlay
self.set_size_request(400, 200) self.set_size_request(400, 200)
self.connect("destroy", self.close_window) self.connect("destroy", self.close_window)
self.connect("delete-event", self.close_window) self.connect("delete-event", self.close_window)
self.placement_window = None self.placement_window = None
self.align_x = None
self.align_y = None
self.init_config() self.init_config()
self.create_gui() self.create_gui()
def present(self): def present_settings(self):
self.show_all() self.show_all()
if not self.floating: if not self.floating:
self.align_x_widget.show() self.align_x_widget.show()
@ -85,7 +99,8 @@ class VoiceSettingsWindow(SettingsWindow):
self.overlay.set_only_speaking(self.only_speaking) self.overlay.set_only_speaking(self.only_speaking)
self.overlay.set_highlight_self(self.highlight_self) self.overlay.set_highlight_self(self.highlight_self)
self.overlay.set_icon_only(self.icon_only) 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_vert_edge_padding(self.vert_edge_padding)
self.overlay.set_horz_edge_padding(self.horz_edge_padding) self.overlay.set_horz_edge_padding(self.horz_edge_padding)
self.overlay.set_order(self.order) self.overlay.set_order(self.order)
@ -337,17 +352,15 @@ class VoiceSettingsWindow(SettingsWindow):
self.add(box) self.add(box)
pass
def change_placement(self, button): def change_placement(self, button):
if self.placement_window: if self.placement_window:
(x,y,w,h) = self.placement_window.get_coords() (x, y, w, h) = self.placement_window.get_coords()
self.floating_x = x self.floating_x = x
self.floating_y = y self.floating_y = y
self.floating_w = w self.floating_w = w
self.floating_h = h self.floating_h = h
self.overlay.set_floating(True, x, y, w, h) self.overlay.set_floating(True, x, y, w, h)
self.save_config self.save_config()
if not self.overlay.is_wayland: if not self.overlay.is_wayland:
button.set_label("Place Window") button.set_label("Place Window")
self.placement_window.close() self.placement_window.close()
@ -357,12 +370,12 @@ class VoiceSettingsWindow(SettingsWindow):
self.placement_window = DraggableWindowWayland( self.placement_window = DraggableWindowWayland(
x=self.floating_x, y=self.floating_y, x=self.floating_x, y=self.floating_y,
w=self.floating_w, h=self.floating_h, w=self.floating_w, h=self.floating_h,
message="Place & resize this window then press Green!",settings=self) message="Place & resize this window then press Green!", settings=self)
else: else:
self.placement_window = DraggableWindow( self.placement_window = DraggableWindow(
x=self.floating_x, y=self.floating_y, x=self.floating_x, y=self.floating_y,
w=self.floating_w, h=self.floating_h, w=self.floating_w, h=self.floating_h,
message="Place & resize this window then press Save!",settings=self) message="Place & resize this window then press Save!", settings=self)
if not self.overlay.is_wayland: if not self.overlay.is_wayland:
button.set_label("Save this position") button.set_label("Save this position")
@ -448,7 +461,6 @@ class VoiceSettingsWindow(SettingsWindow):
m_s = mon.get_model() m_s = mon.get_model()
self.overlay.set_monitor(button.get_active(), mon) self.overlay.set_monitor(button.get_active(), mon)
self.monitor = m_s self.monitor = m_s
self.save_config() self.save_config()

View file

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