- Added a whole bunch more special cases for gamescope environment

- Piggyback text chat into voice chat when only one window can be shown
- Added 'show on taskbar' option to Core, which should allow screen capture of overlay in X11
- Update guild list in text settings more frequently. Should help with #178
- Repurposed wayland draggable windows for gamescope
- Fixed a bug in floating voice overlay
This commit is contained in:
Trigg 2022-03-14 16:52:47 +00:00
parent 3434dc0e50
commit d01c01b3cf
12 changed files with 169 additions and 105 deletions

View file

@ -37,24 +37,9 @@
</release>
</releases>
<content_rating type="oars-1.0">
<content_attribute id="violence-cartoon">intense</content_attribute>
<content_attribute id="violence-fantasy">intense</content_attribute>
<content_attribute id="violence-realistic">intense</content_attribute>
<content_attribute id="violence-bloodshed">intense</content_attribute>
<content_attribute id="violence-sexual">intense</content_attribute>
<content_attribute id="drugs-alcohol">moderate</content_attribute>
<content_attribute id="drugs-narcotics">moderate</content_attribute>
<content_attribute id="drugs-tobacco">moderate</content_attribute>
<content_attribute id="sex-nudity">intense</content_attribute>
<content_attribute id="sex-themes">intense</content_attribute>
<content_attribute id="language-profanity">intense</content_attribute>
<content_attribute id="language-humor">intense</content_attribute>
<content_attribute id="language-discrimination">intense</content_attribute>
<content_attribute id="social-chat">intense</content_attribute>
<content_attribute id="social-info">intense</content_attribute>
<content_attribute id="social-audio">intense</content_attribute>
<content_attribute id="social-location">intense</content_attribute>
<content_attribute id="social-contacts">intense</content_attribute>
<content_attribute id="money-advertising">intense</content_attribute>
</content_rating>
</component>

View file

@ -67,6 +67,7 @@ class DiscordConnector:
self.request_text_rooms = None
self.request_text_rooms_response = None
self.request_text_rooms_awaiting = 0
self.last_requested_guild = 0
self.rate_limited_channels=[]
@ -381,8 +382,13 @@ class DiscordConnector:
self.set_in_room(thisuser["id"], True)
elif j["data"]["type"] == 0: # Text channel
if self.request_text_rooms_response is not None:
if len(self.request_text_rooms_response) <= j['data']['position']:
# Error. The list of channels has changed since we requested last
self.request_text_rooms_for_guild(self.last_requested_guild)
pass
self.request_text_rooms_response[j['data']
['position']] = j['data']
['position']] = j['data']
if self.current_text == j["data"]["id"]:
self.text = []
for message in j["data"]["messages"]:
@ -605,17 +611,16 @@ class DiscordConnector:
self.voice_overlay.set_connection(self.last_connection)
self.list_altered = False
# Update text list
if self.text_overlay:
if self.text_overlay.popup_style:
self.text_altered = True
if self.text_altered:
self.text_overlay.set_text_list(self.text, self.text_altered)
self.text_altered = False
# Update guilds
self.text_settings.set_guilds(self.guilds)
# Check for changed channel
if self.authed:
self.set_text_channel(self.text_settings.get_channel())
if self.text_overlay.popup_style:
self.text_altered = True
if self.text_altered:
self.text_overlay.set_text_list(self.text, self.text_altered)
self.text_altered = False
# Update guilds
self.text_settings.set_guilds(self.guilds)
# Check for changed channel
if self.authed:
self.set_text_channel(self.text_settings.get_channel())
if self.voice_overlay.needsredraw:
self.voice_overlay.redraw()
@ -665,6 +670,8 @@ class DiscordConnector:
This will be mixed in with 'None' in the list where a voice channel is
"""
if(guild_id == 0):
return
if guild_id in self.guilds:
guild = self.guilds[guild_id]
if "channels" in guild:

View file

@ -47,7 +47,9 @@ class Discover:
logging.info("GameScope session detected. Enabling steam and gamescope integration")
self.steamos = True
self.show_settings_delay = True
Gtk.Settings.get_default().set_property("gtk-application-prefer-dark-theme", Gtk.true)
settings = Gtk.Settings.get_default()
if settings:
settings.set_property("gtk-application-prefer-dark-theme", Gtk.true)
self.create_gui()
@ -125,11 +127,12 @@ class Discover:
"""
Create Systray & associated menu, overlays & settings windows
"""
self.voice_overlay = VoiceOverlayWindow(self)
if self.steamos:
self.text_overlay = None
self.text_overlay = TextOverlayWindow(self, self.voice_overlay)
else:
self.text_overlay = TextOverlayWindow(self)
self.voice_overlay = VoiceOverlayWindow(self)
self.menu = self.make_menu()
self.make_sys_tray_icon(self.menu)
self.settings = MainSettingsWindow(self)
@ -197,6 +200,12 @@ class Discover:
"""
self.settings.present_settings()
def hide_settings(self, _obj=None, _data=None):
"""
Hide settings window
"""
self.settings.close_window()
def close(self, _a=None, _b=None, _c=None):
"""
End of the program
@ -211,6 +220,12 @@ class Discover:
if self.text_overlay:
self.text_overlay.set_force_xshape(force)
def set_show_task(self, visible):
if self.voice_overlay:
self.voice_overlay.set_task(visible)
if self.text_overlay:
self.text_overlay.set_task(visible)
def set_sys_tray_icon_visible(self, visible):
"""
Sets whether the tray icon is visible

View file

@ -20,13 +20,14 @@ try:
gi.require_version('GtkLayerShell', '0.1')
from gi.repository import GtkLayerShell
except (ImportError, ValueError):
GtkLayerShell = None
pass
class DraggableWindowWayland(Gtk.Window):
"""A Wayland full-screen window which can be moved and resized"""
def __init__(self, pos_x=0, pos_y=0, width=300, height=300, message="Message", settings=None):
def __init__(self, pos_x=0, pos_y=0, width=300, height=300, message="Message", settings=None, steamos=False):
Gtk.Window.__init__(self, type=Gtk.WindowType.TOPLEVEL)
self.pos_x = pos_x
self.pos_y = pos_y
@ -47,12 +48,16 @@ class DraggableWindowWayland(Gtk.Window):
self.drag_type = None
self.drag_x = 0
self.drag_y = 0
GtkLayerShell.init_for_window(self)
GtkLayerShell.set_layer(self, GtkLayerShell.Layer.TOP)
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.LEFT, True)
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.RIGHT, True)
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.BOTTOM, True)
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.TOP, True)
if GtkLayerShell:
GtkLayerShell.init_for_window(self)
GtkLayerShell.set_layer(self, GtkLayerShell.Layer.TOP)
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.LEFT, True)
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.RIGHT, True)
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.BOTTOM, True)
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.TOP, True)
if steamos:
self.set_size_request(1280, 800)
self.show_all()
# self.force_location()

View file

@ -24,10 +24,11 @@ class GeneralSettingsWindow(SettingsWindow):
"""Core Settings Tab"""
def __init__(self, discover):
SettingsWindow.__init__(self)
SettingsWindow.__init__(self, discover)
self.discover = discover
self.xshape = None
self.show_sys_tray_icon = None
self.show_task = None
self.set_size_request(400, 200)
self.connect("destroy", self.close_window)
self.connect("delete-event", self.close_window)
@ -45,6 +46,7 @@ class GeneralSettingsWindow(SettingsWindow):
config.read(self.config_file)
self.xshape = config.getboolean("general", "xshape", fallback=False)
self.show_sys_tray_icon = config.getboolean("general", "showsystray", fallback=True)
self.show_task = config.getboolean("general", "showtask", fallback=False)
# Pass all of our config over to the overlay
self.discover.set_force_xshape(self.xshape)
@ -61,6 +63,7 @@ class GeneralSettingsWindow(SettingsWindow):
config.set("general", "xshape", "%d" % (int(self.xshape)))
config.set("general", "showsystray", "yes" if self.show_sys_tray_icon else "no")
config.set("general", "showtask", "yes" if self.show_task else "no")
with open(self.config_file, 'w') as file:
config.write(file)
@ -89,12 +92,20 @@ class GeneralSettingsWindow(SettingsWindow):
show_sys_tray_icon.set_active(self.show_sys_tray_icon)
show_sys_tray_icon.connect("toggled", self.change_show_sys_tray_icon)
# Show taskbar
show_task_label = Gtk.Label.new("Show on taskbar")
show_task = Gtk.CheckButton.new()
show_task.set_active(self.show_task)
show_task.connect("toggled", self.change_show_task)
box.attach(autostart_label, 0, 0, 1, 1)
box.attach(autostart, 1, 0, 1, 1)
box.attach(xshape_label, 0, 1, 1, 1)
box.attach(xshape, 1, 1, 1, 1)
box.attach(show_sys_tray_icon_label, 0, 2, 1, 1)
box.attach(show_sys_tray_icon, 1, 2, 1, 1)
box.attach(show_task_label, 0,3,1,1)
box.attach(show_task,1,3,1,1)
self.add(box)
@ -120,3 +131,11 @@ class GeneralSettingsWindow(SettingsWindow):
self.discover.set_sys_tray_icon_visible(button.get_active())
self.show_sys_tray_icon = button.get_active()
self.save_config()
def change_show_task(self, button):
"""
Show in task bar changed
"""
self.discover.set_show_task(button.get_active())
self.show_task = button.get_active()
self.save_config()

View file

@ -50,7 +50,7 @@ class OverlayWindow(Gtk.Window):
return Gtk.WindowType.TOPLEVEL
return Gtk.WindowType.POPUP
def __init__(self, discover):
def __init__(self, discover, piggyback=None):
Gtk.Window.__init__(self, type=self.detect_type())
self.discover = discover
screen = self.get_screen()
@ -88,26 +88,25 @@ class OverlayWindow(Gtk.Window):
self.set_decorated(True)
self.set_accept_focus(False)
self.set_wayland_state()
if not piggyback:
self.show_all()
if discover.steamos:
display = Display()
atom = display.intern_atom("GAMESCOPE_EXTERNAL_OVERLAY")
opaq = display.intern_atom("_NET_WM_WINDOW_OPACITY")
self.show_all()
if discover.steamos:
self.floating = False
display = Display()
atom = display.intern_atom("GAMESCOPE_EXTERNAL_OVERLAY")
opaq = display.intern_atom("_NET_WM_WINDOW_OPACITY")
topw = display.create_resource_object("window", self.get_toplevel().get_window().get_xid())
topw = display.create_resource_object("window", self.get_toplevel().get_window().get_xid())
topw.change_property(atom,
Xatom.CARDINAL,32,
[1], X.PropModeReplace)
# Keep for reference, but appears to be unnecessary
#topw.change_property(opaq,
# Xatom.CARDINAL,16,
# [0xffff], X.PropModeReplace)
topw.change_property(atom,
Xatom.CARDINAL,32,
[1], X.PropModeReplace)
# Keep for reference, but appears to be unnecessary
#topw.change_property(opaq,
# Xatom.CARDINAL,16,
# [0xffff], X.PropModeReplace)
logging.info("Setting STEAM_EXTERNAL_OVERLAY")
display.sync()
logging.info("Setting STEAM_EXTERNAL_OVERLAY")
display.sync()
self.monitor = 0
self.align_right = True
self.align_vert = 1
@ -115,6 +114,10 @@ class OverlayWindow(Gtk.Window):
self.force_xshape = False
self.context = None
self.autohide=False
self.piggyback=None
self.piggyback_parent=None
if piggyback:
self.set_piggyback(piggyback)
def set_wayland_state(self):
"""
@ -133,6 +136,10 @@ class OverlayWindow(Gtk.Window):
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.BOTTOM, True)
GtkLayerShell.set_anchor(self, GtkLayerShell.Edge.TOP, True)
def set_piggyback(self, other_overlay):
other_overlay.piggyback = self
self.piggyback_parent = other_overlay
def overlay_draw(self, _w, context, data=None):
"""
Draw overlay
@ -156,8 +163,6 @@ class OverlayWindow(Gtk.Window):
self.pos_y = pos_y
self.width = width
self.height = height
if self.discover.steamos:
self.floating = False
self.force_location()
def set_untouchable(self):
@ -184,7 +189,19 @@ class OverlayWindow(Gtk.Window):
"""
On X11 enforce the location and sane defaults
On Wayland just store for later
On Gamescope enforce size of display but only if it's the primary overlay
"""
if self.discover.steamos and not self.piggyback_parent:
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()
width = geometry.width
height = geometry.height
self.resize(width, height)
self.needsredraw = True
return
if not self.is_wayland:
self.set_decorated(False)
self.set_keep_above(True)
@ -222,6 +239,9 @@ class OverlayWindow(Gtk.Window):
"""
self.needsredraw = False
gdkwin = self.get_window()
if self.piggyback_parent:
self.piggyback_parent.redraw()
return
if not self.floating:
(width, height) = self.get_size()
self.width = width
@ -297,7 +317,7 @@ class OverlayWindow(Gtk.Window):
Set if this overlay should be visible
"""
self.enabled = enabled
if enabled and not self.hidden:
if enabled and not self.hidden and not self.piggyback_parent:
self.show_all()
else:
self.hide()
@ -307,3 +327,7 @@ class OverlayWindow(Gtk.Window):
Set Mouseover hide
"""
self.autohide = hide
def set_task(self, visible):
self.set_skip_pager_hint(not visible)
self.set_skip_taskbar_hint(not visible)

View file

@ -36,7 +36,7 @@ class SettingsWindow(Gtk.VBox):
overlay types without copy-and-pasting too much code
"""
def __init__(self):
def __init__(self, discover):
Gtk.VBox.__init__(self)
self.placement_window = None
self.config_dir = None
@ -56,6 +56,7 @@ class SettingsWindow(Gtk.VBox):
self.align_y = None
self.enabled = None
self.autohide = None
self.discover = discover
def init_config(self):
"""
@ -137,26 +138,32 @@ class SettingsWindow(Gtk.VBox):
self.floating_y = pos_y
self.floating_w = width
self.floating_h = height
logging.info("Positioned overlay : %s , %s %s x %s", self.floating_x, self.floating_y, self.floating_w, self.floating_h)
self.overlay.set_floating(True, pos_x, pos_y, width, height)
self.save_config()
if not self.overlay.is_wayland:
if button:
button.set_label("Place Window")
self.placement_window.close()
self.placement_window = None
if self.discover.steamos:
self.discover.show_settings()
else:
if self.overlay.is_wayland:
if self.discover.steamos:
self.discover.hide_settings()
if self.overlay.is_wayland or self.discover.steamos:
self.placement_window = DraggableWindowWayland(
pos_x=self.floating_x, pos_y=self.floating_y,
width=self.floating_w, height=self.floating_h,
message="Place & resize this window then press Green!", settings=self)
message="Place & resize this window then press Green!", settings=self,
steamos = self.discover.steamos)
else:
self.placement_window = DraggableWindow(
pos_x=self.floating_x, pos_y=self.floating_y,
width=self.floating_w, height=self.floating_h,
message="Place & resize this window then press Save!", settings=self)
if not self.overlay.is_wayland:
button.set_label("Save this position")
if button:
button.set_label("Save this position")
def change_align_type_edge(self, button):
"""

View file

@ -42,7 +42,6 @@ class MainSettingsWindow(Gtk.Window):
if discover.steamos:
notebook.set_tab_pos(Gtk.PositionType.LEFT)
self.set_default_size(1280, 800)
self.about_settings = AboutSettingsWindow(self.discover)
@ -50,12 +49,12 @@ class MainSettingsWindow(Gtk.Window):
notebook.append_page(self.about_settings)
notebook.set_tab_label(self.about_settings, about_label)
self.voice_settings = VoiceSettingsWindow(self.voice_overlay)
self.voice_settings = VoiceSettingsWindow(self.voice_overlay, discover)
voice_label = Gtk.Label.new("Voice")
notebook.append_page(self.voice_settings)
notebook.set_tab_label(self.voice_settings, voice_label)
self.text_settings = TextSettingsWindow(self.text_overlay)
self.text_settings = TextSettingsWindow(self.text_overlay, discover)
text_label = Gtk.Label.new("Text")
notebook.append_page(self.text_settings)

View file

@ -27,8 +27,8 @@ from gi.repository import Pango, PangoCairo
class TextOverlayWindow(OverlayWindow):
"""Overlay window for voice"""
def __init__(self, discover):
OverlayWindow.__init__(self, discover)
def __init__(self, discover, piggyback = None):
OverlayWindow.__init__(self, discover, piggyback)
self.text_spacing = 4
self.content = []
self.text_font = None
@ -179,6 +179,8 @@ class TextOverlayWindow(OverlayWindow):
"""
Draw the overlay
"""
if not self.enabled:
return
self.context = context
context.set_antialias(cairo.ANTIALIAS_GOOD)
(width, height) = self.get_size()
@ -187,11 +189,11 @@ class TextOverlayWindow(OverlayWindow):
context.set_operator(cairo.OPERATOR_SOURCE)
context.paint()
context.save()
if self.is_wayland:
if self.is_wayland or self.piggyback_parent or self.discover.steamos:
# Special case!
# The window is full-screen regardless of what the user has selected. Because Wayland
# The window is full-screen regardless of what the user has selected.
# We need to set a clip and a transform to imitate original behaviour
# Used in wlroots & gamescope
width = self.width
height = self.height
context.translate(self.pos_x, self.pos_y)
@ -236,8 +238,8 @@ class TextOverlayWindow(OverlayWindow):
if current_y <= 0:
# We've done enough
break
if self.is_wayland:
context.restore()
context.restore()
self.context = None
def draw_attach(self, pos_y, url):
"""

View file

@ -28,8 +28,8 @@ GUILD_DEFAULT_VALUE = "0"
class TextSettingsWindow(SettingsWindow):
"""Text setting tab on settings window"""
def __init__(self, overlay):
SettingsWindow.__init__(self)
def __init__(self, overlay, discover):
SettingsWindow.__init__(self, discover)
self.overlay = overlay
self.placement_window = None
@ -134,6 +134,9 @@ class TextSettingsWindow(SettingsWindow):
else:
self.text_time_widget.hide()
self.text_time_label_widget.hide()
self.update_guild_model()
def update_guild_model(self):
g_model = Gtk.ListStore(str, bool)
self.guild_lookup = []
@ -189,11 +192,16 @@ class TextSettingsWindow(SettingsWindow):
"""
Set the contents of list_guilds
"""
if self.list_guilds == in_list:
return
self.list_guilds = in_list
self.list_guilds_keys = []
logging.warn("Guild list")
logging.warn(in_list)
for key in in_list.keys():
self.list_guilds_keys.append(key)
self.list_guilds_keys.sort()
self.update_guild_model()
def read_config(self):
"""
@ -472,7 +480,6 @@ class TextSettingsWindow(SettingsWindow):
guild_id = self.guild_lookup[button.get_active()]
self.guild = guild_id
self.save_config()
# self.update_channel_model()
self.connector.request_text_rooms_for_guild(self.guild)
def change_popup_style(self, button):

View file

@ -27,8 +27,8 @@ from gi.repository import Pango, PangoCairo
class VoiceOverlayWindow(OverlayWindow):
"""Overlay window for voice"""
def __init__(self, discover):
OverlayWindow.__init__(self, discover)
def __init__(self, discover, piggyback=None):
OverlayWindow.__init__(self, discover, piggyback)
self.avatars = {}
@ -270,7 +270,7 @@ class VoiceOverlayWindow(OverlayWindow):
self.needsredraw = True
def overlay_draw(self, _w, context, _data=None):
def overlay_draw(self, w, context, data=None):
"""
Draw the Overlay
"""
@ -284,16 +284,19 @@ class VoiceOverlayWindow(OverlayWindow):
context.set_operator(cairo.OPERATOR_SOURCE)
context.paint()
context.save()
if self.is_wayland:
if self.piggyback:
self.piggyback.overlay_draw(w, context, data)
if self.is_wayland or self.piggyback_parent or self.discover.steamos:
# Special case!
# The window is full-screen regardless of what the user has selected. Because Wayland
# The window is full-screen regardless of what the user has selected.
# We need to set a clip and a transform to imitate original behaviour
# Used in wlroots & gamescope
width = self.width
height = self.height
context.translate(self.pos_x, self.pos_y)
context.rectangle(0, 0, width, height)
context.clip()
if self.floating:
context.translate(self.pos_x, self.pos_y)
context.rectangle(0, 0, width, height)
context.clip()
context.set_operator(cairo.OPERATOR_OVER)
if not self.connected:
@ -342,12 +345,6 @@ class VoiceOverlayWindow(OverlayWindow):
self.draw_avatar(context, user, current_x)
# Shift the relative position down to next location
current_x += self.avatar_size + self.icon_spacing
if self.is_wayland:
context.restore()
# Don't hold a ref
self.context = None
else:
# Calculate height needed to show overlay
needed_height = (len(self.users_to_draw) * self.avatar_size) + \
@ -365,11 +362,8 @@ class VoiceOverlayWindow(OverlayWindow):
self.draw_avatar(context, user, current_y)
# Shift the relative position down to next location
current_y += self.avatar_size + self.icon_spacing
if self.is_wayland:
context.restore()
# Don't hold a ref
self.context = None
context.restore()
self.context = None
def recv_avatar(self, identifier, pix):
"""

View file

@ -37,8 +37,8 @@ def guild_ids_to_string(guild_ids):
class VoiceSettingsWindow(SettingsWindow):
"""Voice setting tab on settings window"""
def __init__(self, overlay):
SettingsWindow.__init__(self)
def __init__(self, overlay, discover):
SettingsWindow.__init__(self, discover)
self.overlay = overlay
self.set_size_request(400, 200)
self.connect("destroy", self.close_window)
@ -204,10 +204,10 @@ class VoiceSettingsWindow(SettingsWindow):
config.set("main", "horz_edge_padding", "%d" %
(self.horz_edge_padding))
config.set("main", "floating", "%s" % (int(self.floating)))
config.set("main", "floating_x", "%s" % (self.floating_x))
config.set("main", "floating_y", "%s" % (self.floating_y))
config.set("main", "floating_w", "%s" % (self.floating_w))
config.set("main", "floating_h", "%s" % (self.floating_h))
config.set("main", "floating_x", "%s" % (int(self.floating_x)))
config.set("main", "floating_y", "%s" % (int(self.floating_y)))
config.set("main", "floating_w", "%s" % (int(self.floating_w)))
config.set("main", "floating_h", "%s" % (int(self.floating_h)))
config.set("main", "order", "%s" % (self.order))
config.set("main", "horizontal", "%s" % (self.horizontal))
config.set("main", "guild_ids", "%s" % guild_ids_to_string(self.guild_ids))