- 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:
parent
060fc11818
commit
39b1a19e3c
16 changed files with 436 additions and 276 deletions
|
|
@ -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 *
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
||||||
2
setup.py
2
setup.py
|
|
@ -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',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue