|
| 1 | +# -*- coding: utf-8 -*- |
| 2 | + |
| 3 | +# VolumeManager NVDA addon |
| 4 | +# Authors: Danstiv, Beqa gozalishvili |
| 5 | +# Copyright 2019, released under GPL. |
| 6 | + |
| 7 | +import addonHandler |
| 8 | +from comtypes import CLSCTX_ALL |
| 9 | +from ctypes import POINTER, cast |
| 10 | +import globalPluginHandler |
| 11 | +import gui |
| 12 | +from gui.guiHelper import BoxSizerHelper |
| 13 | +import os |
| 14 | +from speech import cancelSpeech |
| 15 | +import sys |
| 16 | +import tones |
| 17 | +import ui |
| 18 | +import wx |
| 19 | +sys.path.append(os.path.dirname(os.path.abspath(__file__))) |
| 20 | +from . import pycaw |
| 21 | +from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume |
| 22 | +del sys.path[-1] |
| 23 | + |
| 24 | +addonHandler.initTranslation() |
| 25 | + |
| 26 | +class Change_volume_dialog(wx.Dialog): |
| 27 | + def __init__(self, parent=None, value=100): |
| 28 | + wx.Dialog.__init__(self, parent, title=_('Set volume')) |
| 29 | + self.main_sizer=BoxSizerHelper(self, wx.VERTICAL) |
| 30 | + self.volume_field=self.main_sizer.addLabeledControl(_('Volume'), wx.SpinCtrl, min=0, max=100, initial=value, style=wx.SP_ARROW_KEYS|wx.TE_PROCESS_ENTER) |
| 31 | + self.Bind(wx.EVT_TEXT_ENTER, self.on_enter, self.volume_field) |
| 32 | + self.btn_sizer=BoxSizerHelper(self, wx.HORIZONTAL) |
| 33 | + self.ok_btn=wx.Button(self, wx.ID_OK, label=_('OK')) |
| 34 | + self.btn_sizer.addItem(self.ok_btn) |
| 35 | + self.Bind(wx.EVT_BUTTON, self.on_ok, self.ok_btn) |
| 36 | + self.cancel_btn=wx.Button(self, wx.ID_CANCEL, label=_('Cancel')) |
| 37 | + self.btn_sizer.addItem(self.cancel_btn) |
| 38 | + self.Bind(wx.EVT_BUTTON, self.on_cancel, self.cancel_btn) |
| 39 | + self.main_sizer.addItem(self.btn_sizer) |
| 40 | + self.SetSizer(self.main_sizer.sizer) |
| 41 | + self.Bind(wx.EVT_SHOW, self.on_show) |
| 42 | + self.Bind(wx.EVT_CLOSE, self.on_close) |
| 43 | + def on_show(self, event): |
| 44 | + self.volume_field.SetFocus() |
| 45 | + self.volume_field.SetSelection(0, -1) |
| 46 | + event.Skip() |
| 47 | + def on_enter(self, event): |
| 48 | + self.set() |
| 49 | + self.Close() |
| 50 | + def on_ok(self, event): |
| 51 | + self.set() |
| 52 | + event.Skip() |
| 53 | + def on_cancel(self, event): |
| 54 | + self.set_all_gestures() |
| 55 | + event.Skip() |
| 56 | + def on_close(self, event): |
| 57 | + self.set_all_gestures() |
| 58 | + event.Skip() |
| 59 | + def set(self): |
| 60 | + self.callback(self.volume_field.GetValue()) |
| 61 | +class GlobalPlugin(globalPluginHandler.GlobalPlugin): |
| 62 | + def __init__(self, *args, **kwargs): |
| 63 | + globalPluginHandler.GlobalPlugin.__init__(self, *args, **kwargs) |
| 64 | + self.enabled=False |
| 65 | + self.app_index=0 |
| 66 | + devices = AudioUtilities.GetSpeakers() |
| 67 | + interface = devices.Activate(IAudioEndpointVolume._iid_, CLSCTX_ALL, None) |
| 68 | + self.master_volume=cast(interface, POINTER(IAudioEndpointVolume)) |
| 69 | + self.master_volume.SetMasterVolume=self.master_volume.SetMasterVolumeLevelScalar |
| 70 | + self.master_volume.GetMasterVolume=self.master_volume.GetMasterVolumeLevelScalar |
| 71 | + self.master_volume.name=_('Master volume') |
| 72 | + self.current_app=self.master_volume |
| 73 | + Change_volume_dialog.callback=self.set_volume |
| 74 | + Change_volume_dialog.set_all_gestures=self.set_all_gestures |
| 75 | + self.standard_gestures={'kb:nvda+shift+v': 'turn', 'kb:volumeDown': 'volume_changed', 'kb:volumeUp': 'volume_changed'} |
| 76 | + self.gestures={'kb:leftArrow': 'move_to_app', 'kb:rightArrow': 'move_to_app', 'kb:upArrow': 'change_volume', 'kb:downArrow': 'change_volume', 'kb:space': 'set_volume', 'kb:m': 'mute_app'} |
| 77 | + self.set_standard_gestures() |
| 78 | + def script_change_volume(self, gesture): |
| 79 | + direction=1 if gesture.logIdentifier.split(':')[-1]=='upArrow' else -1 |
| 80 | + volume=round(self.current_app.GetMasterVolume(), 2) |
| 81 | + if direction == 1: |
| 82 | + if volume>=1.0: |
| 83 | + tones.beep(500, 300) |
| 84 | + return |
| 85 | + volume+=0.01 |
| 86 | + self.current_app.SetMasterVolume(volume, None) |
| 87 | + else: |
| 88 | + if volume<=0.0: |
| 89 | + tones.beep(200, 300) |
| 90 | + return |
| 91 | + volume-=0.01 |
| 92 | + self.current_app.SetMasterVolume(volume, None) |
| 93 | + ui.message(str(int(round(volume*100, 0)))+'%') |
| 94 | + def script_volume_changed(self, gesture): |
| 95 | + gesture.send() |
| 96 | + cancelSpeech() |
| 97 | + ui.message(str(int(round(round(self.master_volume.GetMasterVolume(), 2)*100, 0)))+'%') |
| 98 | + def script_move_to_app(self, gesture): |
| 99 | + direction=1 if gesture.logIdentifier.split(':')[-1]=='rightArrow' else -1 |
| 100 | + l=len(self.apps) |
| 101 | + i=self.app_index |
| 102 | + i=i+1 if direction==1 else i-1 |
| 103 | + if i<0: |
| 104 | + i=l-1 |
| 105 | + if i>=l: |
| 106 | + i=0 |
| 107 | + self.app_index=i |
| 108 | + self.current_app=self.apps[self.app_index] |
| 109 | + ui.message(self.current_app.name + ' ' + str(int(round(round(self.current_app.GetMasterVolume(), 2)*100, 0))) + ' %') |
| 110 | + def script_set_volume(self, gesture): |
| 111 | + self.clearGestureBindings() |
| 112 | + gui.mainFrame._popupSettingsDialog(Change_volume_dialog, int(round(round(self.current_app.GetMasterVolume(), 2)*100, 0))) |
| 113 | + def set_volume(self, volume): |
| 114 | + self.current_app.SetMasterVolume(volume/100.0, None) |
| 115 | + self.set_all_gestures() |
| 116 | + def script_mute_app(self, gesture): |
| 117 | + muteState = self.current_app.GetMute() |
| 118 | + if muteState == 0: |
| 119 | + self.current_app.SetMute(1, None) |
| 120 | + ui.message(_('muted')) |
| 121 | + elif muteState == 1: |
| 122 | + self.current_app.SetMute(0, None) |
| 123 | + ui.message(_('unmuted')) |
| 124 | + def script_turn(self, gesture): |
| 125 | + self.enabled=not self.enabled |
| 126 | + if not self.enabled: |
| 127 | + tones.beep(440, 100) |
| 128 | + self.set_standard_gestures() |
| 129 | + return |
| 130 | + all_sessions=AudioUtilities.GetAllSessions() |
| 131 | + self.apps=[] |
| 132 | + del self.app_index |
| 133 | + self.apps.append(self.master_volume) |
| 134 | + for session in all_sessions: |
| 135 | + if session.Process: |
| 136 | + s=session.SimpleAudioVolume |
| 137 | + s.name=session.Process.name() |
| 138 | + self.apps.append(s) |
| 139 | + if s.name==self.current_app.name: |
| 140 | + self.app_index=len(self.apps)-1 |
| 141 | + if not hasattr(self, 'app_index'): |
| 142 | + self.app_index=0 |
| 143 | + self.current_app=self.apps[self.app_index] |
| 144 | + tones.beep(660, 100) |
| 145 | + self.set_all_gestures() |
| 146 | + def set_standard_gestures(self): |
| 147 | + self.clearGestureBindings() |
| 148 | + self.bindGestures(self.standard_gestures) |
| 149 | + def set_all_gestures(self): |
| 150 | + self.clearGestureBindings() |
| 151 | + self.bindGestures(self.standard_gestures) |
| 152 | + self.bindGestures(self.gestures) |
0 commit comments