1import sys 2import os 3import signal 4import logging 5import time 6from threading import Event 7# This xinit import must happen before any GUI libraries are initialized. 8# pylint: disable=wrong-import-position,wrong-import-order,ungrouped-imports,unused-import 9import ulauncher.utils.xinit # noqa: F401 10 11import gi 12 13# Fixes issue #488 14sys.path.append('/usr/lib/python3.8/site-packages') 15 16gi.require_version('Gtk', '3.0') 17# pylint: disable=wrong-import-position 18from gi.repository import Gtk 19import dbus 20import dbus.service 21from dbus.mainloop.glib import DBusGMainLoop 22 23from ulauncher.config import get_version, get_options, CACHE_DIR, CONFIG_DIR, DATA_DIR 24from ulauncher.utils.decorator.run_async import run_async 25from ulauncher.utils.wayland import is_wayland, is_wayland_compatibility_on 26from ulauncher.ui.windows.UlauncherWindow import UlauncherWindow 27from ulauncher.ui.AppIndicator import AppIndicator 28from ulauncher.utils.Settings import Settings 29from ulauncher.utils.setup_logging import setup_logging 30from ulauncher.api.version import api_version 31 32 33DBUS_SERVICE = 'net.launchpad.ulauncher' 34DBUS_PATH = '/net/launchpad/ulauncher' 35 36 37def _create_dirs(): 38 if not os.path.exists(CONFIG_DIR): 39 os.makedirs(CONFIG_DIR) 40 41 if not os.path.exists(DATA_DIR): 42 os.makedirs(DATA_DIR) 43 44 # make sure ~/.cache/ulauncher exists 45 if not os.path.exists(CACHE_DIR): 46 os.makedirs(CACHE_DIR) 47 48 49class UlauncherDbusService(dbus.service.Object): 50 def __init__(self, window): 51 self.window = window 52 bus_name = dbus.service.BusName(DBUS_SERVICE, bus=dbus.SessionBus()) 53 super().__init__(bus_name, DBUS_PATH) 54 55 @dbus.service.method(DBUS_SERVICE) 56 def toggle_window(self): 57 self.window.toggle_window() 58 59 60# pylint: disable=too-few-public-methods 61class SignalHandler: 62 63 _exit_event = None 64 _app_window = None 65 _logger = None 66 67 def __init__(self, app_window): 68 self._exit_event = Event() 69 self._app_window = app_window 70 self._logger = logging.getLogger('ulauncher') 71 signal.signal(signal.SIGINT, self._exit_gracefully) 72 signal.signal(signal.SIGTERM, self._exit_gracefully) 73 signal.signal(signal.SIGHUP, self._reload_configs) 74 75 def _reload_configs(self, *args): 76 self._logger.info('Received SIGHUP. Reloading configs') 77 self._app_window.init_theme() 78 79 def killed(self): 80 """ 81 :rtype: bool 82 """ 83 return self._exit_event.is_set() 84 85 def _exit_gracefully(self, *args): 86 self._exit_event.set() 87 88 89def main(): 90 """ 91 Main function that starts everything 92 """ 93 94 # start DBus loop 95 DBusGMainLoop(set_as_default=True) 96 bus = dbus.SessionBus() 97 instance = bus.request_name(DBUS_SERVICE) 98 99 if instance != dbus.bus.REQUEST_NAME_REPLY_PRIMARY_OWNER: 100 print( 101 "DBus name already taken. Ulauncher is probably backgrounded. Did you mean `ulauncher-toggle`?", 102 file=sys.stderr 103 ) 104 toggle_window = dbus.SessionBus().get_object(DBUS_SERVICE, DBUS_PATH).get_dbus_method("toggle_window") 105 toggle_window() 106 return 107 108 _create_dirs() 109 110 options = get_options() 111 setup_logging(options) 112 logger = logging.getLogger('ulauncher') 113 logger.info('Ulauncher version %s', get_version()) 114 logger.info('Extension API version %s', api_version) 115 logger.info("GTK+ %s.%s.%s", Gtk.get_major_version(), Gtk.get_minor_version(), Gtk.get_micro_version()) 116 logger.info("Is Wayland: %s", is_wayland()) 117 logger.info("Wayland compatibility: %s", ('on' if is_wayland_compatibility_on() else 'off')) 118 119 # log uncaught exceptions 120 def except_hook(exctype, value, tb): 121 logger.error("Uncaught exception", exc_info=(exctype, value, tb)) 122 123 sys.excepthook = except_hook 124 125 window = UlauncherWindow.get_instance() 126 UlauncherDbusService(window) 127 if not options.hide_window: 128 window.show() 129 130 if Settings.get_instance().get_property('show-indicator-icon'): 131 AppIndicator.get_instance().show() 132 133 # workaround to make Ctrl+C quitting the app 134 signal_handler = SignalHandler(window) 135 gtk_thread = run_async(Gtk.main)() 136 try: 137 while gtk_thread.is_alive() and not signal_handler.killed(): 138 time.sleep(0.5) 139 except KeyboardInterrupt: 140 logger.warning('On KeyboardInterrupt') 141 finally: 142 Gtk.main_quit() 143