From d01c01b3cf522ea742fd9cedf85e90858fb5d38b Mon Sep 17 00:00:00 2001 From: Trigg Date: Mon, 14 Mar 2022 16:52:47 +0000 Subject: [PATCH] - 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 --- discover_overlay.appdata.xml | 15 ----- discover_overlay/discord_connector.py | 31 +++++---- discover_overlay/discover_overlay.py | 21 ++++++- discover_overlay/draggable_window_wayland.py | 19 +++--- discover_overlay/general_settings.py | 21 ++++++- discover_overlay/overlay.py | 66 +++++++++++++------- discover_overlay/settings.py | 21 ++++--- discover_overlay/settings_window.py | 5 +- discover_overlay/text_overlay.py | 16 ++--- discover_overlay/text_settings.py | 13 +++- discover_overlay/voice_overlay.py | 34 +++++----- discover_overlay/voice_settings.py | 12 ++-- 12 files changed, 169 insertions(+), 105 deletions(-) diff --git a/discover_overlay.appdata.xml b/discover_overlay.appdata.xml index 4162d31..4668ca9 100644 --- a/discover_overlay.appdata.xml +++ b/discover_overlay.appdata.xml @@ -37,24 +37,9 @@ - intense - intense - intense - intense - intense - moderate - moderate - moderate - intense - intense - intense - intense - intense intense intense intense - intense intense - intense diff --git a/discover_overlay/discord_connector.py b/discover_overlay/discord_connector.py index 73e73ac..8243628 100644 --- a/discover_overlay/discord_connector.py +++ b/discover_overlay/discord_connector.py @@ -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: diff --git a/discover_overlay/discover_overlay.py b/discover_overlay/discover_overlay.py index 3240152..000507f 100755 --- a/discover_overlay/discover_overlay.py +++ b/discover_overlay/discover_overlay.py @@ -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 diff --git a/discover_overlay/draggable_window_wayland.py b/discover_overlay/draggable_window_wayland.py index 68816f3..ef5011b 100644 --- a/discover_overlay/draggable_window_wayland.py +++ b/discover_overlay/draggable_window_wayland.py @@ -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() diff --git a/discover_overlay/general_settings.py b/discover_overlay/general_settings.py index ffd8b3f..a14f2d7 100644 --- a/discover_overlay/general_settings.py +++ b/discover_overlay/general_settings.py @@ -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() diff --git a/discover_overlay/overlay.py b/discover_overlay/overlay.py index 034f9fe..a5a3fab 100644 --- a/discover_overlay/overlay.py +++ b/discover_overlay/overlay.py @@ -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) diff --git a/discover_overlay/settings.py b/discover_overlay/settings.py index 54f48de..6811644 100644 --- a/discover_overlay/settings.py +++ b/discover_overlay/settings.py @@ -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): """ diff --git a/discover_overlay/settings_window.py b/discover_overlay/settings_window.py index 70de00e..4772bdf 100644 --- a/discover_overlay/settings_window.py +++ b/discover_overlay/settings_window.py @@ -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) diff --git a/discover_overlay/text_overlay.py b/discover_overlay/text_overlay.py index 132b5ef..cca97d2 100644 --- a/discover_overlay/text_overlay.py +++ b/discover_overlay/text_overlay.py @@ -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): """ diff --git a/discover_overlay/text_settings.py b/discover_overlay/text_settings.py index 70b84af..4d6058f 100644 --- a/discover_overlay/text_settings.py +++ b/discover_overlay/text_settings.py @@ -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): diff --git a/discover_overlay/voice_overlay.py b/discover_overlay/voice_overlay.py index 1e1e2aa..9968cf3 100644 --- a/discover_overlay/voice_overlay.py +++ b/discover_overlay/voice_overlay.py @@ -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): """ diff --git a/discover_overlay/voice_settings.py b/discover_overlay/voice_settings.py index 8b4ab4d..1276a2c 100644 --- a/discover_overlay/voice_settings.py +++ b/discover_overlay/voice_settings.py @@ -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))