示例#1
0
def run():
    loop = MainLoop()
    bus = SystemBus(mainloop=dglib.DBusGMainLoop())
    dglib.threads_init()

    bat_object = bus.get_object(
        'org.freedesktop.UPower',
        '/org/freedesktop/UPower/devices/battery_BAT1')
    bat = Interface(bat_object, 'org.freedesktop.DBus.Properties')

    lemonbar = Feeder(i3, bat, bus, bar, display_title)
    lemonbar.render_all()

    pulse = Pulse('event-printer')
    pulse.event_mask_set('sink', 'server')
    pulse.event_callback_set(lemonbar.on_volume_event)
    pulse_thread = Thread(target=pulse.event_listen, args=[0])
    pulse_thread.daemon = True
    pulse_thread.start()

    bat_object.connect_to_signal(
        "PropertiesChanged",
        lemonbar.on_battery_event,
        dbus_interface='org.freedesktop.DBus.Properties')
    bat_thread = Thread(target=loop.run)
    bat_thread.daemon = True
    bat_thread.start()

    sched = BackgroundScheduler(daemon=True)
    sched.add_job(lemonbar.on_timedate_event, 'cron', second=0)
    sched.start()

    def shutdown(caller, e):
        bar.stop_bar(wbak, bar_pid)
        print(e.change)
        exit(0)

    def reload_bar(caller, data):
        global bar_pid
        bar.stop_bar(wbak, bar_pid)
        #print("reloading...")
        bar_pid = bar.start_bar()
        lemonbar.set_outputs()
        lemonbar.render_all(caller=caller, e=data)

    i3.on('workspace::urgent', lemonbar.on_workspace_event)
    i3.on('workspace::empty', lemonbar.on_workspace_event)
    if display_title:
        i3.on('window::title', lemonbar.on_window_title_change)
        i3.on('window::close', lemonbar.on_window_close)
        i3.on('window::focus', lemonbar.on_window_title_change)
        i3.on('workspace::focus', lemonbar.on_workspace_focus)
    else:
        i3.on('workspace::focus', lemonbar.on_workspace_event)
    i3.on('mode', lemonbar.on_binding_mode_change)
    i3.on('output', reload_bar)
    i3.on('shutdown', shutdown)
    i3.main()
示例#2
0
class PulseNerve():
    quit = False

    def __init__(self, cm):
        self.pulse = Pulse()
        self.cm = cm

        self.pulse.event_mask_set('all')
        self.pulse.event_callback_set(self.state_change)
        self.pulse.event_listen(timeout=10)

    def run(self):
        while not self.quit:
            time.sleep(0.1)

    def state_change(self, ev):
        #psil = self.pulse.sink_input_list()
        #props = [i.proplist for i in psil]
        print(ev.index, dir(ev.facility), ev.t)
示例#3
0
class PA2JACK(object):
    jacksink_channel_map = ['aux{}'.format(i) for i in range(16)]
    default_channel_map = ['front-left', 'front-right', 'rear-left' 'rear-right', 'lfe', 'subwoofer', 'side-left', 'side-right']

    def __init__(self,args):

        # get arguments
        if 'allow_reload' in args:
            self.allow_reload = args.allow_reload
        else: self.allow_reload = False
        self.restart = args.internal_restart
        self.num_channels = args.num_channels
        self.pulse_mon = Pulse('pa2jack-monitor') 
        self.pulse_act = Pulse('pa2jack-actor')

        # Get jack sink
        self.jack_sink = self._get_jack_sink()

        # Reload jack module with correct channels
        self.reload_jack_module(self.num_channels)

        # Unload module-remap-sink
        for m in self.pulse_act.module_list():
            if m.name == "module-remap-sink":
                self.pulse_act.module_unload(m.index) 

        # Make our stereo remapping sinks
        self.remap_sink_modules = set([self._new_remap_sink("remap_sink_{}".format(i),start=i*2) for i in range(math.floor(self.num_channels/2))])

        # Get sink indices
        self.remap_sinks = [s.index for s in self.pulse_act.sink_list() if s.owner_module in self.remap_sink_modules]

        # Listen to sink_input events and set handler
        self.pulse_mon.event_mask_set('sink_input')
        self.pulse_mon.event_callback_set(self._pa_event_handler)

    def _get_jack_sink(self):
        try:
            return next(s for s in self.pulse_act.sink_list() if s.name == 'jack_out')
        except StopIteration:
            logging.warn("Couldn't find jack_out sink.")
            return None

    def _default_channel_map(self,n_channels=2,start=0):
        return ",".join(self.default_channel_map[start:start+n_channels])

    def _jack_channel_map(self,start=0,n_channels=2):
        return ",".join(self.jacksink_channel_map[start:start+n_channels])

    def _new_remap_sink(self, sink_name, start=0,n_channels=2):
        logging.debug("making remap-sink: {} ({})".format(sink_name,n_channels))
        default_ch = self._default_channel_map(n_channels)
        aux_channels = self._jack_channel_map(start,n_channels)
        logging.debug("\naux_ch: {}\ndefault_ch: {}".format(aux_channels,default_ch))
        remap_index = self.pulse_act.module_load("module-remap-sink",
                               "sink_name={} master={} channel_map={} master_channel_map={} remix=no".format(
                                            str(sink_name), "jack_out",
                                            default_ch,
                                            aux_channels))
        logging.debug("Done loading module-remap-sink, ind: {}".format(remap_index))
        return remap_index

    def reload_jack_module(self,channels=2,channel_map=jacksink_channel_map):
        """Unload and reload 'module-jack-sink' with specified number of channels.

        Used to dynamically increase the number of available channels if the initial JACK sink cannot accommodate the number of PA streams.
        Will cause a small stutter in the audio, so only enable if that's okay with your environment."""

        #TODO: Insert check for module-default-device-restore // module-rescue-streams before reloading
        try:
            self.pulse_act.module_unload(self.jack_sink.owner_module)
        except:
            logging.warn("Couldn't unload jack module to restart")
        try:
            self.pulse_act.module_load("module-jack-sink","channels={} channel_map={}".format(channels,",".join(channel_map[:channels])))
            self.jack_sink = self._get_jack_sink()
        except:
            logging.warn("Couldn't load JACK module")
            raise NoJackException()

    def _get_dirty_remap_sinks(self):
        return set(map(lambda si: si.sink, self.pulse_act.sink_input_list()))

    def _handle_new_input(self,pa_event):
        # Load remap source module
        logging.debug("New Input: {}".format(pa_event))
        dirty_sinks = self._get_dirty_remap_sinks()
        logging.debug("dirty_sinks: {}".format(dirty_sinks))
        for si in self.remap_sinks:
            if si not in dirty_sinks:
                logging.debug("Moving input {} to sink {}".format(pa_event.index, si))
                self.pulse_act.sink_input_move(pa_event.index,si)
                return

    def _pa_event_handler(self,pa_event):
        """Monitor and distribute Pulseaudio Events.
        """
        logging.debug("Event received: {}".format(pa_event))
        if pa_event.t == 'new' and pa_event.facility == 'source_output':
            self._handle_new_source(pa_event)
        elif pa_event.t == 'new' and pa_event.facility == 'sink_input':
            self._handle_new_input(pa_event)

    def run(self,reloading=False,log=sys.stdout,restart=False):
        """Hacky process monitor catches all exceptions and restarts process if it fails.

        Really you should use a real process monitor, like supervisord or monit."""
        if restart:
            while True:
                try:
                    self.pulse_mon.event_listen()
                except Exception as err:
                    logging.warn("Exception raised in run loop: ".format(err))
                    continue
        else:
            self.pulse_mon.event_listen()
示例#4
0
            if event.t == 'new':
                channels.append(channel(len(channels), event.index))
            else:
                chan = get_channel_by_index(event.index)
                if event.t == 'change':
                    chan.change_event()
                elif event.t == 'remove':
                    del channels[chan.channel_id]
                    for key, chan in enumerate(channels):
                        chan.set_id(key)

        pulse_events.task_done()

midi_input = mido.open_input("nanoKONTROL2 36:0")
midi_output = mido.open_output("nanoKONTROL2 36:0")
pulse.event_mask_set('all')
pulse.event_callback_set(handle_pulse_event)

clear_leds()

# Set up initial channels
for chan_id, sink in enumerate(pulse.sink_input_list()):
    channels.append(channel(chan_id, sink.index))

while True:
    for msg in midi_input.iter_pending():
        if msg.type == 'control_change':
            handle_cc(msg.control, msg.value)
    pulse.event_listen(0.001)
    process_pulse_events()
示例#5
0
class PulseMixer(object):
    profiles = {
        "analog": None,
        "a2dp": None,
        "hsp": None,
    }
    current_profile = None
    active_sink = None

    def __init__(self):
        self.pulse = Pulse('volume-control')

    def introspect(self):
        for k in self.profiles.keys():
           self.profiles[k] = None
        self.current_profile = None

        for k in self.profiles.keys():
            self.profiles[k] = None

        self.cards = self.pulse.card_list()
        self.get_active_sink()
        for card in self.cards:
            for profile in card.profile_list:
                if (card.profile_active.name == profile.name
                    and card.name[:4] == self.active_sink.name[:4]):
                    self.current_profile = profile
                key = PROFILE_MAP.get(profile.name, None)
                if key:
                    self.profiles[key] = profile

    def set_profile(self, key):
        # traverse through cards to determine which ones to turn on/off
        next_profile = self.profiles[key]
        next_card = None
        for card in self.cards:
            for profile in card.profile_list:
                if profile == next_profile:
                    next_card = card
                    break
        off_card = None
        off_profile = None
        for card in self.cards:
            if card == next_card:
                continue
            for profile in card.profile_list:
                if profile.name == PROFILE_OFF:
                    off_card = card
                    off_profile = profile
        if not next_card or not next_profile:
            return
        self.pulse.card_profile_set(next_card, next_profile)
        if off_card and off_profile:
            self.pulse.card_profile_set(off_card, off_profile)

    def get_active_sink(self):
        sink = None
        sink_inputs = self.pulse.sink_input_list()
        # check if a sink input is connected to a sink, if no, use default
        if len(sink_inputs):
            sink_id = sink_inputs[0].sink
            sinks = self.pulse.sink_list()
            sink = next((s for s in sinks if s.index == sink_id), None)
        if sink is None:
            info = self.pulse.server_info()
            sink = self.pulse.get_sink_by_name(info.default_sink_name)
        self.active_sink = sink
        return self.active_sink

    def get_sink_volume_and_mute(self):
        mute = True
        volume = 0
        if self.active_sink:
            volume = self.pulse.volume_get_all_chans(self.active_sink)
            volume = min(max(volume, 0), 1) * 100
            mute = self.active_sink.mute
        return volume, mute

    def set_volume(self, value):
        if self.active_sink:
            self.pulse.volume_set_all_chans(self.active_sink, value / 100.0)

    def change_volume(self, value):
        if self.active_sink:
            volume = self.pulse.volume_get_all_chans(self.active_sink)
            volume += value / 100.0
            volume = min(max(volume, 0), 1)
            self.pulse.volume_set_all_chans(self.active_sink, volume)

    def toggle_mute(self):
        sink = self.active_sink
        sink and self.pulse.mute(sink, not sink.mute)

    def start_listener(self, func):
        self.callback = func
        self.thread = threading.Thread(
            target=self.async_listener
        )
        self.thread.daemon = True
        self.thread.start()

    def async_listener(self):
        self.pulse_d = Pulse('volume-daemon')
        self.pulse_d.event_mask_set('sink')

        # Glib.idle_add is to run the callback in the UI thread
        self.pulse_d.event_callback_set(
            lambda e: GLib.idle_add(self.callback)
        )
        self.pulse_d.event_listen()
示例#6
0
文件: core.py 项目: mishurov/applets
class PulseMixer(object):
    all_profiles = {}
    current_profile = None
    active_sink = None

    def __init__(self):
        self.pulse = Pulse('volume-control')

    def introspect(self):
        self.all_profiles = {}
        self.current_profile = None
        self.cards = self.pulse.card_list()

        for card in self.cards:
            description = card.proplist.get('device.description')
            for profile in card.profile_list:
                #print(profile)
                prof_key = PROFILE_MAP.get(profile.name, None)
                if prof_key and profile.available:
                    key = description + '__' + prof_key
                    self.all_profiles[key] = [card, profile]
                    if (card.profile_active.name == profile.name
                            and self.active_sink
                            and card.name[:4] == self.active_sink.name[:4]):
                        self.current_profile = key

    def set_profile(self, key):
        prof = self.all_profiles[key]
        if not prof:
            return
        card, profile = prof
        for c, p in self.all_profiles.values():
            if c != card:
                self.pulse.card_profile_set(c, PROFILE_OFF)
            elif p == profile:
                self.pulse.card_profile_set(card, profile)

    def get_active_sink(self):
        sink = None
        sink_inputs = self.pulse.sink_input_list()
        # check if a sink input is connected to a sink, if no, use default
        if len(sink_inputs):
            sink_id = sink_inputs[0].sink
            sinks = self.pulse.sink_list()
            sink = next((s for s in sinks if s.index == sink_id), None)
        if sink is None:
            info = self.pulse.server_info()
            if info.default_sink_name == '@DEFAULT_SINK@':
                return None
            sink = self.pulse.get_sink_by_name(info.default_sink_name)
        self.active_sink = sink
        return self.active_sink

    def get_sink_volume_and_mute(self):
        mute = True
        volume = 0
        if self.active_sink:
            volume = self.pulse.volume_get_all_chans(self.active_sink)
            volume = min(max(volume, 0), 1) * 100
            mute = self.active_sink.mute
        return volume, mute

    def set_volume(self, value):
        if self.active_sink:
            self.pulse.volume_set_all_chans(self.active_sink, value / 100.0)

    def change_volume(self, value):
        if self.active_sink:
            volume = self.pulse.volume_get_all_chans(self.active_sink)
            volume += value / 100.0
            volume = min(max(volume, 0), 1)
            self.pulse.volume_set_all_chans(self.active_sink, volume)

    def get_mute(self):
        return self.active_sink.mute

    def toggle_mute(self):
        sink = self.active_sink
        sink and self.pulse.mute(sink, not sink.mute)

    def start_listener(self, func):
        self.callback = func
        self.thread = threading.Thread(target=self.async_listener)
        self.thread.daemon = True
        self.thread.start()

    def async_listener(self):
        self.pulse_d = Pulse('volume-daemon')
        self.pulse_d.event_mask_set('sink', 'card')

        # Glib.idle_add is to run the callback in the UI thread
        self.pulse_d.event_callback_set(self.callback())
        try:
            self.pulse_d.event_listen()
        except PulseDisconnected:
            time.sleep(3)
            self.pulse = Pulse('volume-control')
            self.async_listener()
示例#7
0
class Py3status:
    py3: Py3
    blocks = u"_▁▂▃▄▅▆▇█"
    button_down = 5
    button_mute = 1
    button_up = 4
    format = u"{icon} {percentage}%"
    format_muted = u"{icon} {percentage}%"
    is_input = False
    max_volume = 100
    thresholds = [(0, "good"), (75, "degraded"), (100, "bad")]
    volume_delta = 5

    def __init__(self,
                 sink_name: Optional[str] = None,
                 volume_boost: bool = False):
        """

        :param sink_name:  Sink name to use. Empty uses default sink
        :param volume_boost: Whether to allow setting volume above 1.0 - uses software boost
        """
        self._sink_name = sink_name
        self._sink_info: Optional[PulseSinkInfo]
        self._volume_boost = volume_boost
        self._pulse_connector = Pulse('py3status-pulse-connector',
                                      threading_lock=True)
        self._pulse_connector_lock = threading.Lock()
        self._volume: Optional[Volume] = None
        self._backend_thread = threading.Thread

    def _get_volume_from_backend(self):
        """Get a new sink on every call.

        The sink is not updated when the backed values change.
        Returned volume is the maximum of all available channels.
        """
        sink_name = self._pulse_connector.server_info().default_sink_name
        self._sink_info = self._pulse_connector.get_sink_by_name(sink_name)
        pulse_volume = Volume.from_sink_info(self._sink_info)
        logger.debug(pulse_volume)
        if self._volume != pulse_volume:
            self._volume = pulse_volume
            self.py3.update()

    def _callback(self, ev):
        if ev.t == PulseEventTypeEnum.change and \
                (ev.facility == PulseEventFacilityEnum.server or
                 ev.facility == PulseEventFacilityEnum.sink and ev.index == self._sink_info.index):
            raise PulseLoopStop

    def _pulse_reader(self):
        while True:
            try:
                self._pulse_connector.event_listen()
                self._get_volume_from_backend()
            except PulseDisconnected:
                logger.debug("Pulse disconnected. Stopping reader.")
                break

    def post_config_hook(self):
        self._pulse_connector.connect()
        self._get_volume_from_backend()
        self._pulse_connector.event_mask_set(PulseEventMaskEnum.server,
                                             PulseEventMaskEnum.sink)
        self._pulse_connector.event_callback_set(self._callback)
        self._backend_thread = threading.Thread(
            name="pulse_backend", target=self._pulse_reader).start()

    def kill(self):
        logger.info("Shutting down")
        self._pulse_connector.disconnect()

    def _color_for_output(self) -> str:
        if self._volume is None:
            return self.py3.COLOR_BAD
        if self._volume.mute:
            return self.py3.COLOR_MUTED or self.py3.COLOR_BAD
        return self.py3.threshold_get_color(self._volume.level)

    def _icon_for_output(self) -> str:
        return self.blocks[min(
            len(self.blocks) - 1,
            int(math.ceil(self._volume.level / 100 * (len(self.blocks) - 1))),
        )]

    def _format_output(self) -> Union[str, Composite]:
        return self.py3.safe_format(format_string=self.format_muted
                                    if self._volume.mute else self.format,
                                    param_dict={
                                        "icon": self._icon_for_output(),
                                        "percentage": self._volume.level
                                    })

    def volume_status(self):
        response = {
            "cached_until": self.py3.CACHE_FOREVER,
            "color": self._color_for_output(),
            "full_text": self._format_output()
        }
        return response