summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichele Calgaro <michele.calgaro@yahoo.it>2020-05-20 01:19:55 +0900
committerMichele Calgaro <michele.calgaro@yahoo.it>2020-05-25 13:24:33 +0900
commitb06d352d14d9d917ff23288dd6a9433e4e75f7df (patch)
tree5518cf17eab0c4d4f3d76e02c610deccbea63339
parentc0249fdb66a93f3fd3b413f8c05d455f05ae8cdb (diff)
downloadtdemultimedia-b06d352d14d9d917ff23288dd6a9433e4e75f7df.tar.gz
tdemultimedia-b06d352d14d9d917ff23288dd6a9433e4e75f7df.zip
KMix: 1) added direct DCOP access to the user selected mixer/device channel used as master.
2) fixed bug with volume update 3) unlinked global keyboard shortcuts for volume control to avoid clashing with kmilo. A user can still link them manually if needed. Signed-off-by: Michele Calgaro <michele.calgaro@yahoo.it>
-rw-r--r--kmix/CMakeLists.txt2
-rw-r--r--kmix/Makefile.am4
-rw-r--r--kmix/kmix.cpp172
-rw-r--r--kmix/kmix.h30
-rw-r--r--kmix/kmixIface.h82
-rw-r--r--kmix/mdwslider.cpp20
-rw-r--r--kmix/mixer.cpp76
-rw-r--r--kmix/mixer.h46
-rw-r--r--kmix/mixerIface.h8
9 files changed, 332 insertions, 108 deletions
diff --git a/kmix/CMakeLists.txt b/kmix/CMakeLists.txt
index 08006206..f48d8132 100644
--- a/kmix/CMakeLists.txt
+++ b/kmix/CMakeLists.txt
@@ -60,7 +60,7 @@ tde_add_tdeinit_executable( kmix AUTOMOC
mixset.cpp mixdevice.cpp mixer_backend.cpp ksmallslider.cpp
volume.cpp kledbutton.cpp verticaltext.cpp mixerIface.skel
kmixtoolbox.cpp mixertoolbox.cpp dialogviewconfiguration.cpp
- KMixApp.cpp dialogselectmaster.cpp
+ KMixApp.cpp dialogselectmaster.cpp kmixIface.skel
LINK tdeui-shared ${ALSA_LIBRARIES}
)
diff --git a/kmix/Makefile.am b/kmix/Makefile.am
index 1de7f12d..6f832824 100644
--- a/kmix/Makefile.am
+++ b/kmix/Makefile.am
@@ -11,7 +11,7 @@ noinst_HEADERS = kmix.h kmixdockwidget.h kmixprefdlg.h kmixerwidget.h \
viewbase.h viewoutput.h viewinput.h viewsliders.h viewswitches.h viewsurround.h viewdockareapopup.h viewgrid.h \
mixdevicewidget.h mdwslider.h mdwswitch.h mdwenum.h \
mixer.h mixset.h mixdevice.h mixer_backend.h volume.h kledbutton.h version.h kmixtoolbox.h \
- ksmallslider.h kmixapplet.h mixerIface.h verticaltext.h \
+ ksmallslider.h kmixapplet.h mixerIface.h verticaltext.h kmixIface.h \
KMixApp.h dialogviewconfiguration.h kmixtoolbox.h mixertoolbox.h dialogselectmaster.h
@@ -22,7 +22,7 @@ kmix_la_SOURCES = main.cpp kmix.cpp kmixdockwidget.cpp kmixprefdlg.cpp \
viewsliders.cpp viewgrid.cpp \
mixdevicewidget.cpp mdwslider.cpp mdwswitch.cpp mdwenum.cpp \
kmixerwidget.cpp mixer.cpp mixset.cpp mixdevice.cpp mixer_backend.cpp ksmallslider.cpp \
- volume.cpp kledbutton.cpp verticaltext.cpp mixerIface.skel \
+ volume.cpp kledbutton.cpp verticaltext.cpp mixerIface.skel kmixIface.skel \
kmixtoolbox.cpp mixertoolbox.cpp dialogviewconfiguration.cpp KMixApp.cpp dialogselectmaster.cpp
kmix_la_LIBADD = $(LIB_TDEUI) $(LIB_TDEUTILS) $(LIBALIB) $(LIBOSSAUDIO) $(LIBASOUND) $(LIB_TQT) $(LIB_TDECORE) -lDCOP
diff --git a/kmix/kmix.cpp b/kmix/kmix.cpp
index 5bee4585..69630f61 100644
--- a/kmix/kmix.cpp
+++ b/kmix/kmix.cpp
@@ -61,7 +61,7 @@
* Constructs a mixer window (KMix main window)
*/
KMixWindow::KMixWindow()
- : TDEMainWindow(0, 0, 0, 0), m_showTicks( true ),
+ : DCOPObject("kmix"), TDEMainWindow(0, 0, 0, 0), m_showTicks( true ),
m_dockWidget( 0L )
{
m_visibilityUpdateAllowed = true;
@@ -121,11 +121,11 @@ KMixWindow::initActions()
m_globalAccel = new TDEGlobalAccel(this, "KMix");
m_globalAccel->insert( "Increase volume", i18n( "Increase Volume of Master Channel"), TQString(),
- TDEShortcut(TQString("XF86AudioRaiseVolume")), TDEShortcut(TQString("XF86AudioRaiseVolume")), TQT_TQOBJECT(this), TQT_SLOT( increaseVolume() ) );
+ TDEShortcut(), TDEShortcut(), TQT_TQOBJECT(this), TQT_SLOT( slotIncreaseVolume() ) );
m_globalAccel->insert( "Decrease volume", i18n( "Decrease Volume of Master Channel"), TQString(),
- TDEShortcut(TQString("XF86AudioLowerVolume")), TDEShortcut(TQString("XF86AudioLowerVolume")), TQT_TQOBJECT(this), TQT_SLOT( decreaseVolume() ) );
+ TDEShortcut(), TDEShortcut(), TQT_TQOBJECT(this), TQT_SLOT( slotDecreaseVolume() ) );
m_globalAccel->insert( "Toggle mute", i18n( "Toggle Mute of Master Channel"), TQString(),
- TDEShortcut(TQString("XF86AudioMute")), TDEShortcut(TQString("XF86AudioMute")), TQT_TQOBJECT(this), TQT_SLOT( toggleMuted() ) );
+ TDEShortcut(), TDEShortcut(), TQT_TQOBJECT(this), TQT_SLOT( slotToggleMuted() ) );
m_globalAccel->readSettings();
m_globalAccel->updateConnections();
@@ -619,40 +619,162 @@ KMixWindow::configureGlobalShortcuts()
m_globalAccel->updateConnections();
}
-void
-KMixWindow::toggleMuted()
+// KMixIface DCOP interface methods
+void KMixWindow::setVolume(int percentage)
+{
+ Mixer *mixerMaster = Mixer::masterCard();
+ if (mixerMaster)
+ {
+ mixerMaster->setMasterVolume(percentage);
+ }
+}
+
+void KMixWindow::increaseVolume(int percentage)
{
- Mixer* mixerMaster = Mixer::masterCard();
- if ( mixerMaster != 0 ) {
- MixDevice* md = mixerMaster->masterDevice();
- if ( md != 0 && md->hasMute() ) {
- mixerMaster->toggleMute(md->num());
+ Mixer *mixerMaster = Mixer::masterCard();
+ if (mixerMaster)
+ {
+ MixDevice *md = mixerMaster->masterDevice();
+ if (md)
+ {
+ mixerMaster->increaseVolume(md->num(), percentage);
}
}
}
-void
-KMixWindow::increaseVolume()
+void KMixWindow::decreaseVolume(int percentage)
{
- Mixer* mixerMaster = Mixer::masterCard();
- if ( mixerMaster != 0 ) {
- MixDevice* md = mixerMaster->masterDevice();
- if ( md != 0 ) {
- mixerMaster->increaseVolume(md->num());
+ Mixer *mixerMaster = Mixer::masterCard();
+ if (mixerMaster)
+ {
+ MixDevice *md = mixerMaster->masterDevice();
+ if (md)
+ {
+ mixerMaster->decreaseVolume(md->num(), percentage);
}
}
}
-void
-KMixWindow::decreaseVolume()
+int KMixWindow::volume()
+{
+ Mixer *mixerMaster = Mixer::masterCard();
+ if (mixerMaster)
+ {
+ return mixerMaster->masterVolume();
+ }
+ return -1;
+}
+
+void KMixWindow::setAbsoluteVolume(long absoluteVolume)
+{
+ Mixer *mixerMaster = Mixer::masterCard();
+ if (mixerMaster)
+ {
+ MixDevice *md = mixerMaster->masterDevice();
+ if (md)
+ {
+ return mixerMaster->setAbsoluteVolume(md->num(), absoluteVolume);
+ }
+ }
+}
+
+long KMixWindow::absoluteVolume()
+{
+ Mixer *mixerMaster = Mixer::masterCard();
+ if (mixerMaster)
+ {
+ MixDevice *md = mixerMaster->masterDevice();
+ if (md)
+ {
+ return mixerMaster->absoluteVolume(md->num());
+ }
+ }
+ return -1L;
+}
+
+long KMixWindow::absoluteVolumeMin()
+{
+ Mixer *mixerMaster = Mixer::masterCard();
+ if (mixerMaster)
+ {
+ MixDevice *md = mixerMaster->masterDevice();
+ if (md)
+ {
+ return mixerMaster->absoluteVolumeMin(md->num());
+ }
+ }
+ return -1L;
+}
+
+long KMixWindow::absoluteVolumeMax()
{
- Mixer* mixerMaster = Mixer::masterCard();
- if ( mixerMaster != 0 ) {
- MixDevice* md = mixerMaster->masterDevice();
- if ( md != 0 ) {
- mixerMaster->decreaseVolume(md->num());
+ Mixer *mixerMaster = Mixer::masterCard();
+ if (mixerMaster)
+ {
+ MixDevice *md = mixerMaster->masterDevice();
+ if (md)
+ {
+ return mixerMaster->absoluteVolumeMax(md->num());
}
}
+ return -1L;
+}
+
+void KMixWindow::setMute(bool on)
+{
+ Mixer *mixerMaster = Mixer::masterCard();
+ if (mixerMaster)
+ {
+ mixerMaster->setMasterMute(on);
+ }
+}
+
+void KMixWindow::toggleMute()
+{
+ Mixer *mixerMaster = Mixer::masterCard();
+ if (mixerMaster)
+ {
+ mixerMaster->toggleMasterMute();
+ }
+}
+
+bool KMixWindow::mute()
+{
+ Mixer *mixerMaster = Mixer::masterCard();
+ if (mixerMaster)
+ {
+ return mixerMaster->masterMute();
+ }
+ return true;
+}
+
+TQString KMixWindow::mixerName()
+{
+ Mixer *mixerMaster = Mixer::masterCard();
+ if (mixerMaster)
+ {
+ return mixerMaster->mixerName();
+ }
+ return TQString::null;
+}
+
+int KMixWindow::deviceIndex()
+{
+ Mixer *mixerMaster = Mixer::masterCard();
+ if (mixerMaster)
+ {
+ return mixerMaster->masterDeviceIndex();
+ }
+ return -1;
+}
+
+void KMixWindow::setBalance(int balance)
+{
+ Mixer *mixerMaster = Mixer::masterCard();
+ if (mixerMaster)
+ {
+ mixerMaster->setBalance(balance);
+ }
}
#include "kmix.moc"
diff --git a/kmix/kmix.h b/kmix/kmix.h
index e04aee43..e24c457a 100644
--- a/kmix/kmix.h
+++ b/kmix/kmix.h
@@ -49,18 +49,38 @@ class Mixer;
#include "mixer.h"
#include "mixdevicewidget.h"
+#include "kmixIface.h"
class
-KMixWindow : public TDEMainWindow
+KMixWindow : public TDEMainWindow, virtual public KMixIface
{
Q_OBJECT
-
public:
KMixWindow();
~KMixWindow();
+ // Additional functions for DCOP interface
+ void setVolume(int percentage);
+ void increaseVolume(int percentage);
+ void decreaseVolume(int percentage);
+ int volume();
+
+ void setAbsoluteVolume(long absoluteVolume);
+ long absoluteVolume();
+ long absoluteVolumeMin();
+ long absoluteVolumeMax();
+
+ void setMute(bool on);
+ void toggleMute();
+ bool mute();
+
+ TQString mixerName();
+ int deviceIndex();
+
+ void setBalance(int balance);
+
protected slots:
void saveSettings();
@@ -130,9 +150,9 @@ KMixWindow : public TDEMainWindow
void slotHWInfo();
void showSelectedMixer( int mixer );
void configureGlobalShortcuts();
- void toggleMuted();
- void increaseVolume();
- void decreaseVolume();
+ void slotToggleMuted() { toggleMute(); }
+ void slotIncreaseVolume() { increaseVolume(5); }
+ void slotDecreaseVolume() { decreaseVolume(5); }
};
#endif // KMIX_H
diff --git a/kmix/kmixIface.h b/kmix/kmixIface.h
new file mode 100644
index 00000000..6d314d1b
--- /dev/null
+++ b/kmix/kmixIface.h
@@ -0,0 +1,82 @@
+#ifndef __KMIX_IFACE_H
+#define __KMIX_IFACE_H
+
+#include <dcopobject.h>
+
+/**
+ Provides a subset of MixerIface DCOP interface tailored to specifically
+ act onto the current device/channel selected by the user as main channel
+*/
+
+class KMixIface : virtual public DCOPObject
+{
+ K_DCOP
+
+k_dcop:
+ /**
+ Sets the volume to the percentage specified in the parameter.
+ */
+ virtual void setVolume(int percentage)=0;
+ /**
+ Increase the volume by the percentage specified in the parameter.
+ */
+ virtual void increaseVolume(int percentage)=0;
+ /**
+ Decrease the volume by the percentage specified in the parameter.
+ */
+ virtual void decreaseVolume(int percentage)=0;
+ /**
+ Returns the volume of the device (as a percentage, 0..100).
+ */
+ virtual int volume()=0;
+
+ /**
+ Sets the absolute volume of the device. Lower bound is absoluteVolumeMin(),
+ upper bound is absoluteVolumeMax().
+ */
+ virtual void setAbsoluteVolume(long absoluteVolume)=0;
+ /**
+ Returns the absolute volume of the device. The volume is in the range of
+ absoluteVolumeMin() <= absoluteVolume() <= absoluteVolumeMax()
+ */
+ virtual long absoluteVolume()=0;
+ /**
+ Returns the absolute maximum volume of the device.
+ */
+ virtual long absoluteVolumeMin()=0;
+ /**
+ Returns the absolute minimum volume of the device.
+ */
+ virtual long absoluteVolumeMax()=0;
+
+ /**
+ Mutes or unmutes the specified device.
+ */
+ virtual void setMute(bool on)=0;
+ /**
+ Toggles mute-state for the given device.
+ */
+ virtual void toggleMute()=0;
+ /**
+ Returns if the given device is muted or not. If no device is
+ available, it is reported as muted.
+ */
+ virtual bool mute()=0;
+
+ /**
+ Returns the name of the mixer.
+ */
+ virtual TQString mixerName()=0;
+ /**
+ Returns the index of the master device
+ */
+ virtual int deviceIndex()=0;
+
+ /**
+ Sets the balance of the device (negative means balanced to the left
+ speaker and positive to the right one)
+ */
+ virtual void setBalance(int balance)=0;
+};
+
+#endif
diff --git a/kmix/mdwslider.cpp b/kmix/mdwslider.cpp
index 042f3723..6b4c3f51 100644
--- a/kmix/mdwslider.cpp
+++ b/kmix/mdwslider.cpp
@@ -760,15 +760,7 @@ void MDWSlider::setDisabled( bool value )
*/
void MDWSlider::increaseVolume()
{
- Volume vol = m_mixdevice->getVolume();
- long inc = vol.maxVolume() / 20;
- if ( inc == 0 )
- inc = 1;
- for ( int i = 0; i < vol.count(); i++ ) {
- long newVal = (vol[i]) + inc;
- m_mixdevice->setVolume( i, newVal < vol.maxVolume() ? newVal : vol.maxVolume() );
- }
- m_mixer->commitVolumeChange(m_mixdevice);
+ m_mixer->increaseVolume(m_mixdevice->num(), 5);
}
/**
@@ -777,15 +769,7 @@ void MDWSlider::increaseVolume()
*/
void MDWSlider::decreaseVolume()
{
- Volume vol = m_mixdevice->getVolume();
- long inc = vol.maxVolume() / 20;
- if ( inc == 0 )
- inc = 1;
- for ( int i = 0; i < vol.count(); i++ ) {
- long newVal = (vol[i]) - inc;
- m_mixdevice->setVolume( i, newVal > 0 ? newVal : 0 );
- }
- m_mixer->commitVolumeChange(m_mixdevice);
+ m_mixer->decreaseVolume(m_mixdevice->num(), 5);
}
diff --git a/kmix/mixer.cpp b/kmix/mixer.cpp
index 9e3aa359..0cc4fbcf 100644
--- a/kmix/mixer.cpp
+++ b/kmix/mixer.cpp
@@ -514,6 +514,9 @@ void Mixer::setVolume( int deviceidx, int percentage )
// @todo The next call doesn't handle negative volumes correctly.
vol.setAllVolumes( (percentage*vol.maxVolume())/100 );
_mixerBackend->writeVolumeToHW(deviceidx, vol);
+ // Make sure volume reading is synced
+ readSetFromHWforceUpdate();
+ TQTimer::singleShot(50, this, TQT_SLOT(readSetFromHW()));
}
/**
@@ -579,12 +582,15 @@ void Mixer::setAbsoluteVolume( int deviceidx, long absoluteVolume ) {
Volume vol=mixdev->getVolume();
vol.setAllVolumes( absoluteVolume );
_mixerBackend->writeVolumeToHW(deviceidx, vol);
+ // Make sure volume reading is synced
+ readSetFromHWforceUpdate();
+ TQTimer::singleShot(50, this, TQT_SLOT(readSetFromHW()));
}
// @dcop , especially for use in KMilo
-long Mixer::absoluteVolume( int deviceidx )
+long Mixer::absoluteVolume(int deviceidx)
{
- MixDevice *mixdev= mixDeviceByType( deviceidx );
+ MixDevice *mixdev= mixDeviceByType(deviceidx);
if (!mixdev) return 0;
Volume vol=mixdev->getVolume();
@@ -593,9 +599,9 @@ long Mixer::absoluteVolume( int deviceidx )
}
// @dcop , especially for use in KMilo
-long Mixer::absoluteVolumeMax( int deviceidx )
+long Mixer::absoluteVolumeMax(int deviceidx)
{
- MixDevice *mixdev= mixDeviceByType( deviceidx );
+ MixDevice *mixdev= mixDeviceByType(deviceidx);
if (!mixdev) return 0;
Volume vol=mixdev->getVolume();
@@ -604,9 +610,9 @@ long Mixer::absoluteVolumeMax( int deviceidx )
}
// @dcop , especially for use in KMilo
-long Mixer::absoluteVolumeMin( int deviceidx )
+long Mixer::absoluteVolumeMin(int deviceidx)
{
- MixDevice *mixdev= mixDeviceByType( deviceidx );
+ MixDevice *mixdev= mixDeviceByType(deviceidx);
if (!mixdev) return 0;
Volume vol=mixdev->getVolume();
@@ -626,44 +632,54 @@ int Mixer::masterVolume()
}
// @dcop
-void Mixer::increaseVolume( int deviceidx )
+void Mixer::increaseVolume(int deviceidx, int percentage)
{
- MixDevice *mixdev= mixDeviceByType( deviceidx );
- if (mixdev != 0)
+ MixDevice *mixdev= mixDeviceByType(deviceidx);
+ if (mixdev && percentage > 0)
{
Volume vol = mixdev->getVolume();
- long inc = vol.maxVolume() / 20;
- if (inc == 0)
- {
- inc = 1;
- }
- for (int i = 0; i < vol.count(); i++)
+ long maxVol = vol.maxVolume();
+ if (maxVol > 0)
{
- long newVal = (vol[i]) + inc;
- mixdev->setVolume(i, newVal < vol.maxVolume() ? newVal : vol.maxVolume());
+ for (int i = 0; i < vol.count(); i++)
+ {
+ double perc = 100.0 * vol[i] / maxVol;
+ perc += percentage;
+ if (perc > 100.0)
+ {
+ perc = 100.0;
+ }
+ long newVal = (long)(perc * maxVol / 100.0);
+ mixdev->setVolume(i, newVal);
+ }
+ commitVolumeChange(mixdev);
}
- commitVolumeChange(mixdev);
}
}
// @dcop
-void Mixer::decreaseVolume( int deviceidx )
+void Mixer::decreaseVolume(int deviceidx, int percentage)
{
- MixDevice *mixdev= mixDeviceByType( deviceidx );
- if (mixdev != 0)
+ MixDevice *mixdev= mixDeviceByType(deviceidx);
+ if (mixdev && percentage > 0)
{
Volume vol = mixdev->getVolume();
- long inc = vol.maxVolume() / 20;
- if (inc == 0)
- {
- inc = 1;
- }
- for (int i = 0; i < vol.count(); i++)
+ long maxVol = vol.maxVolume();
+ if (maxVol > 0)
{
- long newVal = (vol[i]) - inc;
- mixdev->setVolume(i, newVal > 0 ? newVal : 0);
+ for (int i = 0; i < vol.count(); i++)
+ {
+ double perc = 100.0 * vol[i] / maxVol;
+ perc -= percentage;
+ if (perc < 0.0)
+ {
+ perc = 0.0;
+ }
+ long newVal = (long)(perc * maxVol / 100.0);
+ mixdev->setVolume(i, newVal);
+ }
+ commitVolumeChange(mixdev);
}
- commitVolumeChange(mixdev);
}
}
diff --git a/kmix/mixer.h b/kmix/mixer.h
index 80610c59..47b49ebb 100644
--- a/kmix/mixer.h
+++ b/kmix/mixer.h
@@ -113,29 +113,29 @@ class Mixer : public TQObject, virtual public MixerIface
void setMasterDevice(TQString&);
/// DCOP oriented methods (look at mixerIface.h for the descriptions)
- virtual void setVolume( int deviceidx, int percentage );
- virtual void setAbsoluteVolume( int deviceidx, long absoluteVolume );
- virtual void setMasterVolume( int percentage );
-
- virtual void increaseVolume( int deviceidx );
- virtual void decreaseVolume( int deviceidx );
-
- virtual long absoluteVolume( int deviceidx );
- virtual long absoluteVolumeMin( int deviceidx );
- virtual long absoluteVolumeMax( int deviceidx );
- virtual int volume( int deviceidx );
- virtual int masterVolume();
- virtual int masterDeviceIndex();
-
- virtual void setMute( int deviceidx, bool on );
- virtual void setMasterMute( bool on );
- virtual bool mute( int deviceidx );
- virtual bool masterMute();
- virtual void toggleMute( int deviceidx );
- virtual void toggleMasterMute();
- virtual bool isRecordSource( int deviceidx );
-
- virtual bool isAvailableDevice( int deviceidx );
+ void setVolume( int deviceidx, int percentage );
+ void setAbsoluteVolume( int deviceidx, long absoluteVolume );
+ void setMasterVolume( int percentage );
+
+ void increaseVolume( int deviceidx, int percentage );
+ void decreaseVolume( int deviceidx, int percentage );
+
+ long absoluteVolume( int deviceidx );
+ long absoluteVolumeMin( int deviceidx );
+ long absoluteVolumeMax( int deviceidx );
+ int volume( int deviceidx );
+ int masterVolume();
+ int masterDeviceIndex();
+
+ void setMute( int deviceidx, bool on );
+ void setMasterMute( bool on );
+ bool mute( int deviceidx );
+ bool masterMute();
+ void toggleMute( int deviceidx );
+ void toggleMasterMute();
+ bool isRecordSource( int deviceidx );
+
+ bool isAvailableDevice( int deviceidx );
void commitVolumeChange( MixDevice* md );
diff --git a/kmix/mixerIface.h b/kmix/mixerIface.h
index ef8bbbf5..10386cdb 100644
--- a/kmix/mixerIface.h
+++ b/kmix/mixerIface.h
@@ -20,13 +20,13 @@ k_dcop:
virtual void setMasterVolume( int percentage )=0;
/**
- Increase the volume of the given device by a 5% .
+ Increase the volume of the given device by a given percentage.
*/
- virtual void increaseVolume( int deviceidx )=0;
+ virtual void increaseVolume( int deviceidx, int percentage )=0;
/**
- Decrease the volume of the given device by a 5% .
+ Decrease the volume of the given device by a a given percentage.
*/
- virtual void decreaseVolume( int deviceidx )=0;
+ virtual void decreaseVolume( int deviceidx, int percentage )=0;
/**
Returns the volume of the device (as a percentage, 0..100).