mirror of
https://github.com/libratbag/libratbag.git
synced 2026-11-07 15:14:38 +01:00
Merge ratbagd into libratbag
This commit is contained in:
12
ratbagd/.gitignore
vendored
Normal file
12
ratbagd/.gitignore
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
*.a
|
||||
*.log
|
||||
*.o
|
||||
*.stamp
|
||||
*.swp
|
||||
*.trs
|
||||
*~
|
||||
.dirstamp
|
||||
.libs/
|
||||
/*.tar.xz
|
||||
tags
|
||||
__pycache__
|
||||
21
ratbagd/COPYING
Normal file
21
ratbagd/COPYING
Normal file
@@ -0,0 +1,21 @@
|
||||
Copyright © 2015 David Herrmann <dh.herrmann@gmail.com>
|
||||
Copyright © 2016 Red hat, Inc
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the next
|
||||
paragraph) shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
170
ratbagd/README.md
Normal file
170
ratbagd/README.md
Normal file
@@ -0,0 +1,170 @@
|
||||
ratbagd
|
||||
=======
|
||||
|
||||
ratbagd is a system daemon to introspect and modify configurable mice.
|
||||
|
||||
ratbagd uses libratbag to access mice and exports the features over a DBus
|
||||
API to unprivileged proceses. ratbagd needs permissions to access device
|
||||
nodes (primarily /dev/hidraw nodes) and usually needs to run as root.
|
||||
|
||||
ratbagd is a relatively thin wrapper arond libratbag. If a device is not
|
||||
detected or does not function as expected, the issue is usually in
|
||||
libratbag. libratbag can be found at
|
||||
https://github.com/libratbag/libratbag
|
||||
|
||||
The DBus-Interface
|
||||
------------------
|
||||
|
||||
Note: **the DBus interface is subject to change**
|
||||
|
||||
Interfaces:
|
||||
- org.freedesktop.ratbag1.Manager
|
||||
- org.freedesktop.ratbag1.Device
|
||||
- org.freedesktop.ratbag1.Profile
|
||||
- org.freedesktop.ratbag1.Resolution
|
||||
- org.freedesktop.ratbag1.Button
|
||||
- org.freedesktop.ratbag1.Led
|
||||
|
||||
The **org.freedesktop.ratbag1.Manager** interface provides:
|
||||
- Properties:
|
||||
- Devices -> array of object paths with interface Device
|
||||
- Signals:
|
||||
- DeviceNew -> new device available, carries object path
|
||||
- DeviceRemoved -> device removed, carries object path
|
||||
|
||||
The **org.freedesktop.ratbag1.Device** interface provides:
|
||||
- Properties:
|
||||
- Id -> unique ID of this device
|
||||
- Capabilities -> array of uints with the capabilities enum from libratbag
|
||||
- Description -> device name
|
||||
- Svg -> device SVG name (file only)
|
||||
- SvgPath -> device SVG name (full absolute path)
|
||||
- Profiles -> array of object paths with interface Profile
|
||||
- ActiveProfile -> index of the currently active profile in Profiles
|
||||
- Methods:
|
||||
- GetProfileByIndex(uint) -> returns the object path for the given index
|
||||
|
||||
The **org.freedesktop.ratbag1.Profile** interface provides:
|
||||
- Properties:
|
||||
- Index -> index of the profile
|
||||
- Resolutions -> array of object paths with interface Resolution
|
||||
- Buttons -> array of object paths with interface Button
|
||||
- Leds -> array of object paths with interface Led
|
||||
- ActiveResolution -> index of the currently active resolution in Resolutions
|
||||
- DefaultResolution -> index of the default resolution in Resolutions
|
||||
- Methods:
|
||||
- SetActive(void) -> set this profile to be the active profile
|
||||
- GetResolutionByIndex(uint) -> returns the object path for the given index
|
||||
- Signals:
|
||||
- ActiveProfileChanged -> active profile changed, carries index of the new active profile
|
||||
|
||||
The **org.freedesktop.ratbag1.Resolution** interface provides:
|
||||
- Properties:
|
||||
- Index -> index of the resolution
|
||||
- Capabilities -> array of uints with the capabilities enum from libratbag
|
||||
- XResolution -> uint for the x resolution assigned to this entry
|
||||
- YResolution -> uint for the y resolution assigned to this entry
|
||||
- ReportRate -> uint for the report rate in Hz assigned to this entry
|
||||
- Methods:
|
||||
- SetResolution(uint, uint) -> x/y resolution to assign
|
||||
- SetReportRate(uint) -> uint for the report rate to assign
|
||||
- SetDefault -> set this resolution to be the default
|
||||
- Signals:
|
||||
- ActiveResolutionChanged -> active resolution changed, carries index of the new active resolution
|
||||
- DefaultResolutionChanged -> default resolution changed, carries index of the new default resolution
|
||||
|
||||
The **org.freedesktop.ratbag1.Button** interface provides:
|
||||
- Properties:
|
||||
- Index -> index of the button
|
||||
- Type -> string describing the button type
|
||||
- ButtonMapping -> uint of the current button mapping (if mapping to button)
|
||||
- SpecialMapping -> string of the current special mapping (if mapped to special)
|
||||
- KeyMapping -> array of uints, first entry is the keycode, other entries, if any, are modifiers (if mapped to key)
|
||||
- ActionType -> string describing the action type of the button ("none", "button", "key", "special", "macro", "unknown"). This decides which \*Mapping property has a value
|
||||
- ActionTypes -> array of strings, possible values for ActionType
|
||||
- Methods:
|
||||
- SetButtonMapping(uint) -> set the button mapping to the given button
|
||||
- SetSpecialMapping(string) -> set the button mapping to the given special entry
|
||||
- SetKeyMapping(uint[]) -> set the key mapping, first entry is the keycode, other entries, if any, are modifier keycodes
|
||||
- Disable(void) -> disable this button
|
||||
|
||||
The **org.freedesktop.ratbag1.Led** interface provides:
|
||||
- Properties:
|
||||
- Index -> index of the LED
|
||||
- Mode -> uint mapping to the mode enum from libratbag
|
||||
- Type -> string describing the LED type
|
||||
- Color -> uint triplet (RGB) of the LED's color
|
||||
- EffectRate -> the effect rate in Hz, possible values are in the range 100 - 20000
|
||||
- Brightness -> the brightness of the LED, possible values are in the range 0 - 255
|
||||
- Methods:
|
||||
- SetMode(uint) -> set the mode to the given mode enum value from libratbag
|
||||
- SetColor((uuu)) -> set the color to the given uint triplet (RGB)
|
||||
- SetEffectRate(uint) -> set the effect rate in Hz, possible values are in the range 100 - 20000
|
||||
- SetBrightness(int) -> set the brightness, possible values are in the range 0 - 255
|
||||
|
||||
For easier debugging, objects paths are constructed from the device. e.g.
|
||||
`/org/freedesktop/ratbag/button/event5/p0/b10` is the button interface for
|
||||
button 10 on profile 0 on event5. The naming is subject to change. Do not
|
||||
rely on a constructed object path in your application.
|
||||
|
||||
Running ratbagd
|
||||
---------------
|
||||
|
||||
ratbagd is intended to run as dbus-activated systemd service. This requires
|
||||
installation of the following files:
|
||||
|
||||
sudo cp dbus/org.freedesktop.ratbag1.conf /etc/dbus-1/system.d/org.freedesktop.ratbag1.conf
|
||||
sudo cp builddir/org.freedesktop.ratbag1.service /etc/dbus-1/system-services/org.freedesktop.ratbag1.conf
|
||||
sudo cp builddir/ratbagd.service /etc/systemd/system/ratbagd.service
|
||||
|
||||
The files are installed into the prefix by `ninja install`, see also the
|
||||
configure-time options `-Dsystemd-unit-dir` and `-Ddbus-root-dir` (see
|
||||
"Compiling ratbagd" below). Developers are encouraged to simply symlink to the
|
||||
files in the git repository. If you used any other build directory than
|
||||
`builddir`, adjust accordingly.
|
||||
|
||||
For the files to take effect, you should run
|
||||
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl reload dbus.service
|
||||
|
||||
And finally, to enable the service:
|
||||
|
||||
sudo systemctl enable ratbagd.service
|
||||
|
||||
This places the required symlink into the systemd directory so that dbus
|
||||
activation is possible.
|
||||
|
||||
Compiling ratbagd
|
||||
-----------------
|
||||
|
||||
ratbagd uses the meson build system (see http://mesonbuild.com) which in
|
||||
turn uses ninja to invoke the compiler (`ninja` may be `ninja-build` on your
|
||||
distribution). From a fresh git checkout, run the following commands to init
|
||||
the repository:
|
||||
|
||||
meson builddir --prefix=/usr/
|
||||
|
||||
And to build or re-build after code-changes, run:
|
||||
|
||||
ninja -C builddir
|
||||
sudo ninja -C builddir install
|
||||
|
||||
Note: `builddir` is the build output directory and can be changed to any
|
||||
other directory name. To set configure-time options, use e.g.
|
||||
|
||||
mesonconf builddir -Denable-documentation=false
|
||||
|
||||
Run `mesonconf builddir` to list the options.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
ratbagd is licensed under the MIT license.
|
||||
|
||||
> Permission is hereby granted, free of charge, to any person obtaining a
|
||||
> copy of this software and associated documentation files (the "Software"),
|
||||
> to deal in the Software without restriction, including without limitation
|
||||
> the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
> and/or sell copies of the Software, and to permit persons to whom the
|
||||
> Software is furnished to do so, subject to the following conditions: [...]
|
||||
20
ratbagd/dbus/org.freedesktop.ratbag1.conf
Normal file
20
ratbagd/dbus/org.freedesktop.ratbag1.conf
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0"?> <!--*-nxml-*-->
|
||||
<!DOCTYPE busconfig PUBLIC
|
||||
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||
|
||||
<busconfig>
|
||||
|
||||
<policy user="root">
|
||||
<allow own="org.freedesktop.ratbag1"/>
|
||||
<allow send_destination="org.freedesktop.ratbag1"/>
|
||||
<allow receive_sender="org.freedesktop.ratbag1"/>
|
||||
</policy>
|
||||
|
||||
<!-- allow everyone to talk to the daemon -->
|
||||
<policy context="default">
|
||||
<allow send_destination="org.freedesktop.ratbag1"/>
|
||||
<allow receive_sender="org.freedesktop.ratbag1"/>
|
||||
</policy>
|
||||
|
||||
</busconfig>
|
||||
5
ratbagd/dbus/org.freedesktop.ratbag1.service.in
Normal file
5
ratbagd/dbus/org.freedesktop.ratbag1.service.in
Normal file
@@ -0,0 +1,5 @@
|
||||
[D-BUS Service]
|
||||
Name=org.freedesktop.ratbag1
|
||||
Exec=@bindir@/ratbagd
|
||||
User=root
|
||||
SystemdService=dbus-org.freedesktop.ratbag1.service
|
||||
75
ratbagd/doc/ratbagctl.1
Normal file
75
ratbagd/doc/ratbagctl.1
Normal file
@@ -0,0 +1,75 @@
|
||||
.TH ratbagctl 1 "Apr 13, 2016" ratbagctl
|
||||
.SH NAME
|
||||
ratbagctl \- inspect and modify configurable mice
|
||||
.SH SYNOPSIS
|
||||
.B ratbagctl
|
||||
.RB [ \-h ]
|
||||
.RB [ \-V ]
|
||||
.br
|
||||
.B ratbagctl list-devices
|
||||
.br
|
||||
.B ratbagctl show-device
|
||||
.RI < device >
|
||||
.br
|
||||
.B ratbagctl show-profile
|
||||
.RI < device "> <" profile >
|
||||
.br
|
||||
.B ratbagctl show-resolution
|
||||
.RI < device "> <" profile "> <" resolution >
|
||||
.br
|
||||
.B ratbagctl show-button
|
||||
.RI < device "> <" profile "> <" button >
|
||||
.SH DESCRIPTION
|
||||
.SS Common options
|
||||
.TP
|
||||
.BR \-h
|
||||
displays a short help message (it can be used with any form of the
|
||||
.B ratbagctl
|
||||
command).
|
||||
.TP
|
||||
.BR \-V
|
||||
displays the version of
|
||||
.B ratbagctl
|
||||
and exits.
|
||||
.SS Listing devices
|
||||
.TP
|
||||
.B list-devices
|
||||
lists all identified devices on the system.
|
||||
.SS Displaying a device
|
||||
.TP
|
||||
.BR show-device " <" \fIdevice\fP >
|
||||
displays the device information for the given
|
||||
.I device
|
||||
(as identified by
|
||||
.BR list-devices ).
|
||||
.SS Displaying profiles
|
||||
.TP
|
||||
.IR \fBshow-profile\fP " <" device "> <" profile >
|
||||
displays the profile information for the given
|
||||
.I device
|
||||
and
|
||||
.IR "profile index" .
|
||||
.SS Displaying resolution information
|
||||
.TP
|
||||
.IR \fBshow-resolution\fP " <" device "> <" profile "> <" resolution >
|
||||
displays the resolution information for the given
|
||||
.IR device ,
|
||||
.IR "profile index" ,
|
||||
and
|
||||
.IR "resolution index" .
|
||||
.SS Displaying button information
|
||||
.TP
|
||||
.IR \fBshow-button\fP " <" device "> <" profile "> <" button >
|
||||
displays the resolution information for the given
|
||||
.IR device ,
|
||||
.IR "profile index" ,
|
||||
and
|
||||
.IR "button index" .
|
||||
.SH SEE ALSO
|
||||
.BR ratbagd (8)
|
||||
.SH AUTHORS
|
||||
.B ratbagctl
|
||||
was written by David Herrmann, Peter Hutterer and Benjamin Tissoires.
|
||||
.PP
|
||||
This manual page was written by Stephen Kitt <skitt@debian.org> for
|
||||
the Debian GNU/Linux system (but may be used by others).
|
||||
19
ratbagd/doc/ratbagd.8
Normal file
19
ratbagd/doc/ratbagd.8
Normal file
@@ -0,0 +1,19 @@
|
||||
.TH ratbagd 8 "Apr 13, 2016" ratbagd
|
||||
.SH NAME
|
||||
ratbagd \- system daemon to introspect and modify configurable mice
|
||||
.SH SYNOPSIS
|
||||
.B ratbagd
|
||||
.RB [ \-\-verbose ]
|
||||
.SH DESCRIPTION
|
||||
.B ratbagd
|
||||
starts the daemon. It shouldn't be invoked directly;
|
||||
.B ratbagd
|
||||
is normally started through DBus activation.
|
||||
.SH SEE ALSO
|
||||
.BR ratbagctl (1)
|
||||
.SH AUTHORS
|
||||
.B ratbagd
|
||||
was written by David Herrmann, Peter Hutterer and Benjamin Tissoires.
|
||||
.PP
|
||||
This manual page was written by Stephen Kitt <skitt@debian.org> for
|
||||
the Debian GNU/Linux system (but may be used by others).
|
||||
141
ratbagd/meson.build
Normal file
141
ratbagd/meson.build
Normal file
@@ -0,0 +1,141 @@
|
||||
project('ratbagd', 'c',
|
||||
version : '0.4',
|
||||
license : 'MIT/Expat',
|
||||
meson_version : '>= 0.40.0',
|
||||
default_options : [ 'c_std=gnu99', 'warning_level=2' ])
|
||||
|
||||
ratbagd_version = meson.project_version().split('.')
|
||||
|
||||
# Compiler setup
|
||||
cc = meson.get_compiler('c')
|
||||
cflags = [
|
||||
'-Wno-inline',
|
||||
'-Wundef',
|
||||
'-Wformat=2',
|
||||
'-Wformat-security',
|
||||
'-Wformat-nonliteral',
|
||||
'-Wlogical-op',
|
||||
'-Wsign-compare',
|
||||
'-Wmissing-include-dirs',
|
||||
'-Wold-style-definition',
|
||||
'-Wpointer-arith',
|
||||
'-Winit-self',
|
||||
'-Wdeclaration-after-statement',
|
||||
'-Wfloat-equal',
|
||||
'-Wsuggest-attribute=noreturn',
|
||||
'-Wmissing-prototypes',
|
||||
'-Wstrict-prototypes',
|
||||
'-Wredundant-decls',
|
||||
'-Wmissing-declarations',
|
||||
'-Wmissing-noreturn',
|
||||
'-Wendif-labels',
|
||||
'-Wstrict-aliasing=2',
|
||||
'-Wwrite-strings',
|
||||
'-Wno-long-long',
|
||||
'-Wno-overlength-strings',
|
||||
'-Wno-unused-parameter',
|
||||
'-Wno-missing-field-initializers',
|
||||
'-Wno-unused-result',
|
||||
'-Werror=overflow',
|
||||
'-Wdate-time',
|
||||
'-Wnested-externs',
|
||||
]
|
||||
add_project_arguments(cflags, language: 'c')
|
||||
add_project_arguments('-D_GNU_SOURCE', language : 'c')
|
||||
|
||||
# Initialize config.h, to be added to in the various options below, config.h
|
||||
# is generated at the end of this file
|
||||
config_h = configuration_data()
|
||||
|
||||
# dependencies
|
||||
pkgconfig = import('pkgconfig')
|
||||
dep_udev = dependency('libudev')
|
||||
dep_systemd = dependency('libsystemd', version : '>=227')
|
||||
dep_libratbag = dependency('libratbag', version : '>= 0.6')
|
||||
dep_lm = cc.find_library('m')
|
||||
|
||||
libratbag_data_dir = dep_libratbag.get_pkgconfig_variable('pkgdatadir')
|
||||
config_h.set_quoted('LIBRATBAG_DATA_DIR', libratbag_data_dir)
|
||||
|
||||
#### libshared.a ####
|
||||
src_libshared = [
|
||||
'src/shared-macro.h',
|
||||
'src/shared-rbtree.h',
|
||||
'src/shared-rbtree.c',
|
||||
]
|
||||
|
||||
deps_libshared = [
|
||||
dep_lm,
|
||||
]
|
||||
|
||||
lib_libshared = static_library('shared',
|
||||
src_libshared,
|
||||
dependencies : deps_libshared
|
||||
)
|
||||
dep_libshared = declare_dependency(link_with: lib_libshared)
|
||||
|
||||
#### ratbagd ####
|
||||
src_ratbagd = [
|
||||
'src/ratbagd.h',
|
||||
'src/ratbagd.c',
|
||||
'src/ratbagd-led.c',
|
||||
'src/ratbagd-button.c',
|
||||
'src/ratbagd-device.c',
|
||||
'src/ratbagd-profile.c',
|
||||
'src/ratbagd-resolution.c'
|
||||
]
|
||||
|
||||
deps_ratbagd = [
|
||||
dep_udev,
|
||||
dep_systemd,
|
||||
dep_libratbag,
|
||||
dep_libshared,
|
||||
]
|
||||
|
||||
executable('ratbagd',
|
||||
src_ratbagd,
|
||||
dependencies : deps_ratbagd,
|
||||
include_directories : include_directories('src'),
|
||||
install : true,
|
||||
)
|
||||
|
||||
install_man('doc/ratbagd.8')
|
||||
|
||||
#### unit file ####
|
||||
unitdir = get_option('systemd-unit-dir')
|
||||
if unitdir == ''
|
||||
unitdir = get_option('libdir') + '/systemd/system'
|
||||
endif
|
||||
|
||||
config_bindir = configuration_data()
|
||||
config_bindir.set('bindir', join_paths(get_option('prefix'), get_option('bindir')))
|
||||
|
||||
configure_file(input : 'ratbagd.service.in',
|
||||
output : 'ratbagd.service',
|
||||
configuration : config_bindir,
|
||||
install_dir : unitdir)
|
||||
|
||||
dbusdir = get_option('dbus-root-dir')
|
||||
if dbusdir == ''
|
||||
dbusdir = join_paths(get_option('datadir'), 'dbus-1')
|
||||
endif
|
||||
|
||||
configure_file(input : 'dbus/org.freedesktop.ratbag1.service.in',
|
||||
output : 'org.freedesktop.ratbag1.service',
|
||||
configuration : config_bindir,
|
||||
install_dir : join_paths(dbusdir, 'system-services'))
|
||||
install_data('dbus/org.freedesktop.ratbag1.conf',
|
||||
configuration : config_bindir,
|
||||
install_dir : join_paths(dbusdir, 'system.d'))
|
||||
|
||||
#### tools ####
|
||||
config_ratbagctl = configuration_data()
|
||||
config_ratbagctl.set('version', meson.project_version())
|
||||
configure_file(input : 'tools/ratbagctl.in',
|
||||
output : 'ratbagctl',
|
||||
configuration : config_ratbagctl,
|
||||
install_dir : get_option('bindir'))
|
||||
install_man('doc/ratbagctl.1')
|
||||
|
||||
#### output files ####
|
||||
configure_file(output: 'config.h', install: false, configuration: config_h)
|
||||
9
ratbagd/meson_options.txt
Normal file
9
ratbagd/meson_options.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
option('systemd-unit-dir',
|
||||
type : 'string',
|
||||
default : '',
|
||||
description : 'systemd unit directory [default=$libdir/systemd/system]')
|
||||
|
||||
option('dbus-root-dir',
|
||||
type : 'string',
|
||||
default : '',
|
||||
description : 'dbus service directory [default=$datadir/dbus-1]')
|
||||
11
ratbagd/ratbagd.service.in
Normal file
11
ratbagd/ratbagd.service.in
Normal file
@@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=Daemon to introspect and modify configurable mice
|
||||
|
||||
[Service]
|
||||
Type=dbus
|
||||
BusName=org.freedesktop.ratbag1
|
||||
ExecStart=@bindir@/ratbagd
|
||||
Restart=on-abort
|
||||
|
||||
[Install]
|
||||
Alias=dbus-org.freedesktop.ratbag1.service
|
||||
14
ratbagd/src/org.freedesktop.ratbag1.conf
Normal file
14
ratbagd/src/org.freedesktop.ratbag1.conf
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0"?> <!--*-nxml-*-->
|
||||
<!DOCTYPE busconfig PUBLIC
|
||||
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||
|
||||
<busconfig>
|
||||
|
||||
<policy user="root">
|
||||
<allow own="org.freedesktop.ratbag1"/>
|
||||
<allow send_destination="org.freedesktop.ratbag1"/>
|
||||
<allow receive_sender="org.freedesktop.ratbag1"/>
|
||||
</policy>
|
||||
|
||||
</busconfig>
|
||||
567
ratbagd/src/ratbagd-button.c
Normal file
567
ratbagd/src/ratbagd-button.c
Normal file
@@ -0,0 +1,567 @@
|
||||
/***
|
||||
This file is part of ratbagd.
|
||||
|
||||
Copyright 2016 Red Hat, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the next
|
||||
paragraph) shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
***/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <libratbag.h>
|
||||
#include <libudev.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <systemd/sd-event.h>
|
||||
#include <linux/input.h>
|
||||
#include "ratbagd.h"
|
||||
#include "shared-macro.h"
|
||||
|
||||
struct ratbagd_button {
|
||||
struct ratbag_button *lib_button;
|
||||
unsigned int index;
|
||||
char *path;
|
||||
};
|
||||
|
||||
static int ratbagd_button_get_type(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_button *button = userdata;
|
||||
const char *type = NULL;
|
||||
enum ratbag_button_type t;
|
||||
|
||||
t = ratbag_button_get_type(button->lib_button);
|
||||
|
||||
switch(t) {
|
||||
default:
|
||||
log_error("Unknown button type %d\n", t);
|
||||
/* fallthrough */
|
||||
case RATBAG_BUTTON_TYPE_UNKNOWN:
|
||||
type = "unknown";
|
||||
break;
|
||||
case RATBAG_BUTTON_TYPE_LEFT:
|
||||
type = "left";
|
||||
break;
|
||||
case RATBAG_BUTTON_TYPE_MIDDLE:
|
||||
type = "middle";
|
||||
break;
|
||||
case RATBAG_BUTTON_TYPE_RIGHT:
|
||||
type = "right";
|
||||
break;
|
||||
case RATBAG_BUTTON_TYPE_THUMB:
|
||||
type = "thumb";
|
||||
break;
|
||||
case RATBAG_BUTTON_TYPE_THUMB2:
|
||||
type = "thumb2";
|
||||
break;
|
||||
case RATBAG_BUTTON_TYPE_THUMB3:
|
||||
type = "thumb3";
|
||||
break;
|
||||
case RATBAG_BUTTON_TYPE_THUMB4:
|
||||
type = "thumb4";
|
||||
break;
|
||||
case RATBAG_BUTTON_TYPE_WHEEL_LEFT:
|
||||
type = "wheel-left";
|
||||
break;
|
||||
case RATBAG_BUTTON_TYPE_WHEEL_RIGHT:
|
||||
type = "wheel-right";
|
||||
break;
|
||||
case RATBAG_BUTTON_TYPE_WHEEL_CLICK:
|
||||
type = "wheel-click";
|
||||
break;
|
||||
case RATBAG_BUTTON_TYPE_WHEEL_UP:
|
||||
type = "wheel-up";
|
||||
break;
|
||||
case RATBAG_BUTTON_TYPE_WHEEL_DOWN:
|
||||
type = "wheel-down";
|
||||
break;
|
||||
case RATBAG_BUTTON_TYPE_WHEEL_RATCHET_MODE_SHIFT:
|
||||
type = "wheel-ratchet_mode_shift";
|
||||
break;
|
||||
case RATBAG_BUTTON_TYPE_EXTRA:
|
||||
type = "extra";
|
||||
break;
|
||||
case RATBAG_BUTTON_TYPE_SIDE:
|
||||
type = "side";
|
||||
break;
|
||||
case RATBAG_BUTTON_TYPE_PINKIE:
|
||||
type = "pinkie";
|
||||
break;
|
||||
case RATBAG_BUTTON_TYPE_PINKIE2:
|
||||
type = "pinkie2";
|
||||
break;
|
||||
case RATBAG_BUTTON_TYPE_RESOLUTION_CYCLE_UP:
|
||||
type = "resolution-cycle-up";
|
||||
break;
|
||||
case RATBAG_BUTTON_TYPE_RESOLUTION_UP:
|
||||
type = "resolution-up";
|
||||
break;
|
||||
case RATBAG_BUTTON_TYPE_RESOLUTION_DOWN:
|
||||
type = "resolution-down";
|
||||
break;
|
||||
case RATBAG_BUTTON_TYPE_PROFILE_CYCLE_UP:
|
||||
type = "profile-cycle-up";
|
||||
break;
|
||||
case RATBAG_BUTTON_TYPE_PROFILE_UP:
|
||||
type = "profile-up";
|
||||
break;
|
||||
case RATBAG_BUTTON_TYPE_PROFILE_DOWN:
|
||||
type = "profile-down";
|
||||
break;
|
||||
}
|
||||
|
||||
return sd_bus_message_append(reply, "s", type);
|
||||
}
|
||||
|
||||
static int ratbagd_button_get_button(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_button *button = userdata;
|
||||
unsigned int b;
|
||||
|
||||
b = ratbag_button_get_button(button->lib_button);
|
||||
|
||||
return sd_bus_message_append(reply, "u", b);
|
||||
}
|
||||
|
||||
static int ratbagd_button_set_button(sd_bus_message *m,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_button *button = userdata;
|
||||
unsigned int map;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_read(m, "u", &map);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (map == 0 || map > 30)
|
||||
return 0;
|
||||
|
||||
r = ratbag_button_set_button(button->lib_button, map);
|
||||
|
||||
if (r == 0) {
|
||||
sd_bus *bus = sd_bus_message_get_bus(m);
|
||||
sd_bus_emit_properties_changed(bus,
|
||||
button->path,
|
||||
"org.freedesktop.ratbag1.Button",
|
||||
"ButtonMapping",
|
||||
NULL);
|
||||
}
|
||||
|
||||
return sd_bus_reply_method_return(m, "u", r);
|
||||
}
|
||||
|
||||
static int ratbagd_button_get_special(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_button *button = userdata;
|
||||
const char *type;
|
||||
enum ratbag_button_action_special special;
|
||||
|
||||
special = ratbag_button_get_special(button->lib_button);
|
||||
|
||||
switch(special) {
|
||||
default:
|
||||
log_error("Unknown special type %d\n", special);
|
||||
/* fallthrough */
|
||||
case RATBAG_BUTTON_ACTION_SPECIAL_UNKNOWN:
|
||||
type = "unknown";
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_SPECIAL_INVALID:
|
||||
type = "n/a";
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_SPECIAL_DOUBLECLICK:
|
||||
type = "doubleclick";
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_LEFT:
|
||||
type = "wheel-left";
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_RIGHT:
|
||||
type = "wheel-right";
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_UP:
|
||||
type = "wheel-up";
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_DOWN:
|
||||
type = "wheel-down";
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_SPECIAL_RATCHET_MODE_SWITCH:
|
||||
type = "ratchet-mode-switch";
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP:
|
||||
type = "resolution-cycle-up";
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_UP:
|
||||
type = "resolution-up";
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DOWN:
|
||||
type = "resolution-down";
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_ALTERNATE:
|
||||
type = "resolution-alternate";
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DEFAULT:
|
||||
type = "resolution-default";
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP:
|
||||
type = "profile-cycle-up";
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_UP:
|
||||
type = "profile-up";
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_DOWN:
|
||||
type = "profile-down";
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_SPECIAL_SECOND_MODE:
|
||||
type = "second-mode";
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_SPECIAL_BATTERY_LEVEL:
|
||||
type = "battery-level";
|
||||
break;
|
||||
}
|
||||
|
||||
return sd_bus_message_append(reply, "s", type);
|
||||
}
|
||||
|
||||
static int ratbagd_button_set_special(sd_bus_message *m,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_button *button = userdata;
|
||||
const char *s;
|
||||
enum ratbag_button_action_special special;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_read(m, "s", &s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (streq(s, "doubleclick"))
|
||||
special = RATBAG_BUTTON_ACTION_SPECIAL_DOUBLECLICK;
|
||||
else if (streq(s, "wheel-left"))
|
||||
special = RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_LEFT;
|
||||
else if (streq(s, "wheel-right"))
|
||||
special = RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_RIGHT;
|
||||
else if (streq(s, "wheel-up"))
|
||||
special = RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_UP;
|
||||
else if (streq(s, "wheel-down"))
|
||||
special = RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_DOWN;
|
||||
else if (streq(s, "ratchet-mode-switch"))
|
||||
special = RATBAG_BUTTON_ACTION_SPECIAL_RATCHET_MODE_SWITCH;
|
||||
else if (streq(s, "resolution-cycle-up"))
|
||||
special = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP;
|
||||
else if (streq(s, "resolution-up"))
|
||||
special = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_UP;
|
||||
else if (streq(s, "resolution-down"))
|
||||
special = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DOWN;
|
||||
else if (streq(s, "resolution-alternate"))
|
||||
special = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_ALTERNATE;
|
||||
else if (streq(s, "resolution-default"))
|
||||
special = RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DEFAULT;
|
||||
else if (streq(s, "profile-cycle-up"))
|
||||
special = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP;
|
||||
else if (streq(s, "profile-up"))
|
||||
special = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_UP;
|
||||
else if (streq(s, "profile-down"))
|
||||
special = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_DOWN;
|
||||
else if (streq(s, "second-mode"))
|
||||
special = RATBAG_BUTTON_ACTION_SPECIAL_SECOND_MODE;
|
||||
else if (streq(s, "battery-level"))
|
||||
special = RATBAG_BUTTON_ACTION_SPECIAL_BATTERY_LEVEL;
|
||||
else {
|
||||
log_error("Unknown button special action %s\n", s);
|
||||
return sd_bus_reply_method_return(m, "u", -1);
|
||||
}
|
||||
|
||||
r = ratbag_button_set_special(button->lib_button, special);
|
||||
|
||||
if (r == 0) {
|
||||
sd_bus *bus = sd_bus_message_get_bus(m);
|
||||
sd_bus_emit_properties_changed(bus,
|
||||
button->path,
|
||||
"org.freedesktop.ratbag1.Button",
|
||||
"SpecialMapping",
|
||||
NULL);
|
||||
}
|
||||
|
||||
return sd_bus_reply_method_return(m, "u", r);
|
||||
}
|
||||
|
||||
static int ratbagd_button_get_key(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_button *button = userdata;
|
||||
unsigned int key;
|
||||
unsigned int modifiers[10] = {0};
|
||||
size_t nmodifiers = ELEMENTSOF(modifiers);
|
||||
size_t i;
|
||||
int r;
|
||||
|
||||
/* Return an array with the first element the key, the rest
|
||||
are modifiers (or 0 if unset). If no key is set, the array is
|
||||
just [0] */
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "u");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
key = ratbag_button_get_key(button->lib_button,
|
||||
modifiers,
|
||||
&nmodifiers);
|
||||
|
||||
r = sd_bus_message_append(reply, "u", key);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (key == 0)
|
||||
nmodifiers = 0;
|
||||
|
||||
for (i = 0; i < nmodifiers; i++) {
|
||||
r = sd_bus_message_append(reply, "u", modifiers[0]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static int ratbagd_button_set_key(sd_bus_message *m,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_button *button = userdata;
|
||||
unsigned int key;
|
||||
size_t nmodifiers = 10;
|
||||
unsigned int modifiers[nmodifiers];
|
||||
int r;
|
||||
|
||||
/* Expect an array with the first element the key, the rest
|
||||
are modifiers (or 0 if unset). */
|
||||
|
||||
r = sd_bus_message_enter_container(m, 'a', "u");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_read(m, "u", &key);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
nmodifiers = 0;
|
||||
while ((r = sd_bus_message_read_basic(m, 'u',
|
||||
&modifiers[nmodifiers++])) > 0)
|
||||
;
|
||||
|
||||
r = sd_bus_message_exit_container(m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ratbag_button_set_key(button->lib_button, key, modifiers, nmodifiers);
|
||||
|
||||
return sd_bus_reply_method_return(m, "u", r);
|
||||
}
|
||||
|
||||
static int ratbagd_button_get_action_type(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_button *button = userdata;
|
||||
const char *type;
|
||||
|
||||
switch (ratbag_button_get_action_type(button->lib_button)) {
|
||||
case RATBAG_BUTTON_ACTION_TYPE_NONE:
|
||||
type = "none";
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_TYPE_BUTTON:
|
||||
type = "button";
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_TYPE_KEY:
|
||||
type = "key";
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_TYPE_SPECIAL:
|
||||
type = "special";
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_TYPE_MACRO:
|
||||
type = "macro";
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_TYPE_UNKNOWN:
|
||||
type = "unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
return sd_bus_message_append(reply, "s", type);
|
||||
}
|
||||
|
||||
static int ratbagd_button_get_action_types(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_button *button = userdata;
|
||||
const char *types[5] = {NULL};
|
||||
const char **t;
|
||||
int idx = 0;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "s");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ratbag_button_has_action_type(button->lib_button,
|
||||
RATBAG_BUTTON_ACTION_TYPE_BUTTON))
|
||||
types[idx++] = "button";
|
||||
if (ratbag_button_has_action_type(button->lib_button,
|
||||
RATBAG_BUTTON_ACTION_TYPE_KEY))
|
||||
types[idx++] = "key";
|
||||
if (ratbag_button_has_action_type(button->lib_button,
|
||||
RATBAG_BUTTON_ACTION_TYPE_SPECIAL))
|
||||
types[idx++] = "special";
|
||||
if (ratbag_button_has_action_type(button->lib_button,
|
||||
RATBAG_BUTTON_ACTION_TYPE_MACRO))
|
||||
types[idx++] = "macro";
|
||||
|
||||
t = types;
|
||||
while (*t) {
|
||||
r = sd_bus_message_append(reply, "s", *t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
t++;
|
||||
}
|
||||
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static int ratbagd_button_disable(sd_bus_message *m,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_button *button = userdata;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_read(m, "");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ratbag_button_disable(button->lib_button);
|
||||
|
||||
return sd_bus_reply_method_return(m, "u", r);
|
||||
}
|
||||
|
||||
const sd_bus_vtable ratbagd_button_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_PROPERTY("Index", "u", NULL, offsetof(struct ratbagd_button, index), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Type", "s", ratbagd_button_get_type, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ButtonMapping", "u", ratbagd_button_get_button, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("SpecialMapping", "s", ratbagd_button_get_special, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("KeyMapping", "au", ratbagd_button_get_key, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("ActionType", "s", ratbagd_button_get_action_type, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("ActionTypes", "as", ratbagd_button_get_action_types, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
|
||||
SD_BUS_METHOD("SetButtonMapping", "u", "u", ratbagd_button_set_button, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("SetSpecialMapping", "s", "u", ratbagd_button_set_special, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("SetKeyMapping", "au", "u", ratbagd_button_set_key, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("Disable", "", "u", ratbagd_button_disable, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_VTABLE_END,
|
||||
};
|
||||
|
||||
int ratbagd_button_new(struct ratbagd_button **out,
|
||||
struct ratbagd_device *device,
|
||||
struct ratbagd_profile *profile,
|
||||
struct ratbag_button *lib_button,
|
||||
unsigned int index)
|
||||
{
|
||||
_cleanup_(ratbagd_button_freep) struct ratbagd_button *button = NULL;
|
||||
char profile_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1],
|
||||
button_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1];
|
||||
int r;
|
||||
|
||||
assert(out);
|
||||
assert(lib_button);
|
||||
|
||||
button = calloc(1, sizeof(*button));
|
||||
if (!button)
|
||||
return -ENOMEM;
|
||||
|
||||
button->lib_button = lib_button;
|
||||
button->index = index;
|
||||
|
||||
sprintf(profile_buffer, "p%u", ratbagd_profile_get_index(profile));
|
||||
sprintf(button_buffer, "b%u", index);
|
||||
r = sd_bus_path_encode_many(&button->path,
|
||||
"/org/freedesktop/ratbag1/button/%/%/%",
|
||||
ratbagd_device_get_name(device),
|
||||
profile_buffer,
|
||||
button_buffer);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*out = button;
|
||||
button = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *ratbagd_button_get_path(struct ratbagd_button *button)
|
||||
{
|
||||
assert(button);
|
||||
return button->path;
|
||||
}
|
||||
|
||||
struct ratbagd_button *ratbagd_button_free(struct ratbagd_button *button)
|
||||
{
|
||||
if (!button)
|
||||
return NULL;
|
||||
|
||||
button->path = mfree(button->path);
|
||||
button->lib_button = ratbag_button_unref(button->lib_button);
|
||||
|
||||
return mfree(button);
|
||||
}
|
||||
|
||||
583
ratbagd/src/ratbagd-device.c
Normal file
583
ratbagd/src/ratbagd-device.c
Normal file
@@ -0,0 +1,583 @@
|
||||
/***
|
||||
This file is part of ratbagd.
|
||||
|
||||
Copyright 2015 David Herrmann <dh.herrmann@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the next
|
||||
paragraph) shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
***/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <libratbag.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <systemd/sd-event.h>
|
||||
#include "ratbagd.h"
|
||||
#include "shared-macro.h"
|
||||
#include "shared-rbtree.h"
|
||||
|
||||
struct ratbagd_device {
|
||||
struct ratbagd *ctx;
|
||||
RBNode node;
|
||||
char *name;
|
||||
char *path;
|
||||
struct ratbag_device *lib_device;
|
||||
|
||||
sd_bus_slot *profile_vtable_slot;
|
||||
sd_bus_slot *profile_enum_slot;
|
||||
unsigned int n_profiles;
|
||||
struct ratbagd_profile **profiles;
|
||||
};
|
||||
|
||||
#define ratbagd_device_from_node(_ptr) \
|
||||
rbnode_of((_ptr), struct ratbagd_device, node)
|
||||
|
||||
static int ratbagd_device_find_profile(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
void *userdata,
|
||||
void **found,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
_cleanup_(freep) char *name = NULL;
|
||||
struct ratbagd_device *device = userdata;
|
||||
unsigned int index;
|
||||
int r;
|
||||
|
||||
r = sd_bus_path_decode_many(path,
|
||||
"/org/freedesktop/ratbag1/profile/%/p%",
|
||||
NULL,
|
||||
&name);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = safe_atou(name, &index);
|
||||
if (r < 0)
|
||||
return 0;
|
||||
|
||||
if (index >= device->n_profiles || !device->profiles[index])
|
||||
return 0;
|
||||
|
||||
*found = device->profiles[index];
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ratbagd_device_list_profiles(sd_bus *bus,
|
||||
const char *path,
|
||||
void *userdata,
|
||||
char ***paths,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_device *device = userdata;
|
||||
struct ratbagd_profile *profile;
|
||||
char **profiles;
|
||||
unsigned int i;
|
||||
|
||||
profiles = calloc(device->n_profiles + 1, sizeof(char *));
|
||||
if (!profiles)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < device->n_profiles; ++i) {
|
||||
profile = device->profiles[i];
|
||||
if (!profile)
|
||||
continue;
|
||||
|
||||
profiles[i] = strdup(ratbagd_profile_get_path(profile));
|
||||
if (!profiles[i])
|
||||
goto error;
|
||||
}
|
||||
|
||||
profiles[i] = NULL;
|
||||
*paths = profiles;
|
||||
return 1;
|
||||
|
||||
error:
|
||||
for (i = 0; profiles[i]; ++i)
|
||||
free(profiles[i]);
|
||||
free(profiles);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int ratbagd_device_get_device_name(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_device *device = userdata;
|
||||
const char *name;
|
||||
|
||||
name = ratbag_device_get_name(device->lib_device);
|
||||
if (!name) {
|
||||
log_error("Unable to fetch name for %s\n",
|
||||
ratbagd_device_get_name(device));
|
||||
name = "";
|
||||
}
|
||||
|
||||
return sd_bus_message_append(reply, "s", name);
|
||||
}
|
||||
|
||||
static int ratbagd_device_get_svg(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_device *device = userdata;
|
||||
const char *svg;
|
||||
|
||||
svg = ratbag_device_get_svg_name(device->lib_device);
|
||||
if (!svg) {
|
||||
log_error("Unable to fetch SVG for %s\n",
|
||||
ratbagd_device_get_name(device));
|
||||
svg = "";
|
||||
}
|
||||
|
||||
return sd_bus_message_append(reply, "s", svg);
|
||||
}
|
||||
|
||||
static int ratbagd_device_get_svg_path(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_device *device = userdata;
|
||||
char svg_path[PATH_MAX] = {0};
|
||||
const char *svg;
|
||||
|
||||
svg = ratbag_device_get_svg_name(device->lib_device);
|
||||
if (!svg) {
|
||||
log_error("Unable to fetch SVG for %s\n",
|
||||
ratbagd_device_get_name(device));
|
||||
goto out;
|
||||
}
|
||||
|
||||
sprintf(svg_path, "%s/%s", LIBRATBAG_DATA_DIR, svg);
|
||||
|
||||
out:
|
||||
return sd_bus_message_append(reply, "s", svg_path);
|
||||
}
|
||||
|
||||
static int ratbagd_device_get_profiles(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_device *device = userdata;
|
||||
struct ratbagd_profile *profile;
|
||||
unsigned int i;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "o");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (i = 0; i < device->n_profiles; ++i) {
|
||||
profile = device->profiles[i];
|
||||
if (!profile)
|
||||
continue;
|
||||
|
||||
r = sd_bus_message_append(reply,
|
||||
"o",
|
||||
ratbagd_profile_get_path(profile));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static int ratbagd_device_get_active_profile(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_device *device = userdata;
|
||||
struct ratbagd_profile *profile;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < device->n_profiles; ++i) {
|
||||
profile = device->profiles[i];
|
||||
if (!profile)
|
||||
continue;
|
||||
if (!ratbagd_profile_is_active(profile))
|
||||
continue;
|
||||
|
||||
return sd_bus_message_append(reply, "u", i);
|
||||
}
|
||||
|
||||
log_error("Unable to find active profile for %s\n", device->name);
|
||||
return sd_bus_message_append(reply, "u", 0);
|
||||
}
|
||||
|
||||
static int ratbagd_device_get_profile_by_index(sd_bus_message *m,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_device *device = userdata;
|
||||
struct ratbagd_profile *profile;
|
||||
unsigned int index;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_read(m, "u", &index);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (index >= device->n_profiles || !device->profiles[index])
|
||||
return -ENXIO;
|
||||
|
||||
profile = device->profiles[index];
|
||||
return sd_bus_reply_method_return(m, "o",
|
||||
ratbagd_profile_get_path(profile));
|
||||
}
|
||||
|
||||
static int
|
||||
ratbagd_device_get_capabilities(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_device *device = userdata;
|
||||
struct ratbag_device *lib_device = device->lib_device;
|
||||
enum ratbag_device_capability cap;
|
||||
enum ratbag_device_capability caps[] = {
|
||||
RATBAG_DEVICE_CAP_QUERY_CONFIGURATION,
|
||||
RATBAG_DEVICE_CAP_RESOLUTION,
|
||||
RATBAG_DEVICE_CAP_SWITCHABLE_RESOLUTION,
|
||||
RATBAG_DEVICE_CAP_PROFILE,
|
||||
RATBAG_DEVICE_CAP_SWITCHABLE_PROFILE,
|
||||
RATBAG_DEVICE_CAP_DISABLE_PROFILE,
|
||||
RATBAG_DEVICE_CAP_DEFAULT_PROFILE,
|
||||
RATBAG_DEVICE_CAP_BUTTON,
|
||||
RATBAG_DEVICE_CAP_BUTTON_KEY,
|
||||
RATBAG_DEVICE_CAP_BUTTON_MACROS,
|
||||
RATBAG_DEVICE_CAP_LED,
|
||||
};
|
||||
int r;
|
||||
size_t i;
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "u");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (i = 0; i < ELEMENTSOF(caps); i++) {
|
||||
cap = caps[i];
|
||||
if (ratbag_device_has_capability(lib_device, cap)) {
|
||||
r = sd_bus_message_append(reply, "u", cap);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
const sd_bus_vtable ratbagd_device_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_PROPERTY("Id", "s", NULL, offsetof(struct ratbagd_device, name), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Capabilities", "au", ratbagd_device_get_capabilities, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Name", "s", ratbagd_device_get_device_name, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Svg", "s", ratbagd_device_get_svg, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("SvgPath", "s", ratbagd_device_get_svg_path, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Profiles", "ao", ratbagd_device_get_profiles, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ActiveProfile", "u", ratbagd_device_get_active_profile, 0, 0),
|
||||
SD_BUS_METHOD("GetProfileByIndex", "u", "o", ratbagd_device_get_profile_by_index, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_VTABLE_END,
|
||||
};
|
||||
|
||||
int ratbagd_device_new(struct ratbagd_device **out,
|
||||
struct ratbagd *ctx,
|
||||
const char *name,
|
||||
struct ratbag_device *lib_device)
|
||||
{
|
||||
_cleanup_(ratbagd_device_freep) struct ratbagd_device *device = NULL;
|
||||
struct ratbag_profile *profile;
|
||||
unsigned int i;
|
||||
int r;
|
||||
|
||||
assert(out);
|
||||
assert(ctx);
|
||||
assert(name);
|
||||
|
||||
device = calloc(1, sizeof(*device));
|
||||
if (!device)
|
||||
return -ENOMEM;
|
||||
|
||||
device->ctx = ctx;
|
||||
rbnode_init(&device->node);
|
||||
device->lib_device = ratbag_device_ref(lib_device);
|
||||
|
||||
device->name = strdup(name);
|
||||
if (!device->name)
|
||||
return -ENOMEM;
|
||||
|
||||
r = sd_bus_path_encode("/org/freedesktop/ratbag1/device",
|
||||
device->name,
|
||||
&device->path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
device->n_profiles = ratbag_device_get_num_profiles(device->lib_device);
|
||||
device->profiles = calloc(device->n_profiles, sizeof(*device->profiles));
|
||||
if (!device->profiles)
|
||||
return -ENOMEM;
|
||||
|
||||
log_verbose("%s: \"%s\", %d profiles\n",
|
||||
name,
|
||||
ratbag_device_get_name(lib_device),
|
||||
device->n_profiles);
|
||||
|
||||
for (i = 0; i < device->n_profiles; ++i) {
|
||||
profile = ratbag_device_get_profile(device->lib_device, i);
|
||||
if (!profile)
|
||||
continue;
|
||||
|
||||
r = ratbagd_profile_new(&device->profiles[i],
|
||||
device,
|
||||
profile,
|
||||
i);
|
||||
if (r < 0) {
|
||||
errno = -r;
|
||||
log_error("Cannot allocate profile for '%s': %m\n",
|
||||
device->name);
|
||||
}
|
||||
}
|
||||
|
||||
*out = device;
|
||||
device = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ratbagd_device *ratbagd_device_free(struct ratbagd_device *device)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (!device)
|
||||
return NULL;
|
||||
|
||||
assert(!ratbagd_device_linked(device));
|
||||
|
||||
for (i = 0; i < device->n_profiles; ++i)
|
||||
device->profiles[i] = ratbagd_profile_free(device->profiles[i]);
|
||||
|
||||
device->profiles = mfree(device->profiles);
|
||||
device->lib_device = ratbag_device_unref(device->lib_device);
|
||||
device->path = mfree(device->path);
|
||||
device->name = mfree(device->name);
|
||||
|
||||
assert(!device->lib_device); /* ratbag yields !NULL if still pinned */
|
||||
|
||||
return mfree(device);
|
||||
}
|
||||
|
||||
const char *ratbagd_device_get_name(struct ratbagd_device *device)
|
||||
{
|
||||
assert(device);
|
||||
return device->name;
|
||||
}
|
||||
|
||||
const char *ratbagd_device_get_path(struct ratbagd_device *device)
|
||||
{
|
||||
assert(device);
|
||||
return device->path;
|
||||
}
|
||||
|
||||
unsigned int ratbagd_device_get_num_buttons(struct ratbagd_device *device)
|
||||
{
|
||||
assert(device);
|
||||
return ratbag_device_get_num_buttons(device->lib_device);
|
||||
}
|
||||
|
||||
unsigned int ratbagd_device_get_num_leds(struct ratbagd_device *device)
|
||||
{
|
||||
assert(device);
|
||||
return ratbag_device_get_num_leds(device->lib_device);
|
||||
}
|
||||
|
||||
bool ratbagd_device_linked(struct ratbagd_device *device)
|
||||
{
|
||||
return device && rbnode_linked(&device->node);
|
||||
}
|
||||
|
||||
void ratbagd_device_link(struct ratbagd_device *device)
|
||||
{
|
||||
_cleanup_(freep) char *prefix = NULL;
|
||||
struct ratbagd_device *iter;
|
||||
RBNode **node, *parent;
|
||||
int r, v;
|
||||
unsigned int i;
|
||||
|
||||
assert(device);
|
||||
assert(!ratbagd_device_linked(device));
|
||||
|
||||
/* find place to link it to */
|
||||
parent = NULL;
|
||||
node = &device->ctx->device_map.root;
|
||||
while (*node) {
|
||||
parent = *node;
|
||||
iter = ratbagd_device_from_node(parent);
|
||||
v = strcmp(device->name, iter->name);
|
||||
|
||||
/* if there's a duplicate, the caller screwed up */
|
||||
assert(v != 0);
|
||||
|
||||
if (v < 0)
|
||||
node = &parent->left;
|
||||
else /* if (v > 0) */
|
||||
node = &parent->right;
|
||||
}
|
||||
|
||||
/* link into context */
|
||||
rbtree_add(&device->ctx->device_map, parent, node, &device->node);
|
||||
++device->ctx->n_devices;
|
||||
|
||||
/* register profile interfaces */
|
||||
r = sd_bus_path_encode_many(&prefix,
|
||||
"/org/freedesktop/ratbag1/profile/%",
|
||||
device->name);
|
||||
if (r >= 0) {
|
||||
r = sd_bus_add_fallback_vtable(device->ctx->bus,
|
||||
&device->profile_vtable_slot,
|
||||
prefix,
|
||||
"org.freedesktop.ratbag1.Profile",
|
||||
ratbagd_profile_vtable,
|
||||
ratbagd_device_find_profile,
|
||||
device);
|
||||
if (r >= 0)
|
||||
r = sd_bus_add_node_enumerator(device->ctx->bus,
|
||||
&device->profile_enum_slot,
|
||||
prefix,
|
||||
ratbagd_device_list_profiles,
|
||||
device);
|
||||
}
|
||||
if (r < 0) {
|
||||
errno = -r;
|
||||
log_error("Cannot register profile interfaces for '%s': %m\n",
|
||||
device->name);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < device->n_profiles; i++) {
|
||||
r = ratbagd_profile_register_resolutions(device->ctx->bus,
|
||||
device,
|
||||
device->profiles[i]);
|
||||
if (r < 0) {
|
||||
log_error("Cannot register resolutions for '%s': %m\n",
|
||||
device->name);
|
||||
}
|
||||
|
||||
r = ratbagd_profile_register_buttons(device->ctx->bus,
|
||||
device,
|
||||
device->profiles[i]);
|
||||
if (r < 0) {
|
||||
log_error("Cannot register buttons for '%s': %m\n",
|
||||
device->name);
|
||||
}
|
||||
|
||||
r = ratbagd_profile_register_leds(device->ctx->bus,
|
||||
device,
|
||||
device->profiles[i]);
|
||||
if (r < 0) {
|
||||
log_error("Cannot register leds for '%s': %m\n",
|
||||
device->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ratbagd_device_unlink(struct ratbagd_device *device)
|
||||
{
|
||||
if (!ratbagd_device_linked(device))
|
||||
return;
|
||||
|
||||
device->profile_enum_slot = sd_bus_slot_unref(device->profile_enum_slot);
|
||||
device->profile_vtable_slot = sd_bus_slot_unref(device->profile_vtable_slot);
|
||||
|
||||
/* unlink from context */
|
||||
--device->ctx->n_devices;
|
||||
rbtree_remove(&device->ctx->device_map, &device->node);
|
||||
rbnode_init(&device->node);
|
||||
}
|
||||
|
||||
struct ratbagd_device *ratbagd_device_lookup(struct ratbagd *ctx,
|
||||
const char *name)
|
||||
{
|
||||
struct ratbagd_device *device;
|
||||
RBNode *node;
|
||||
int v;
|
||||
|
||||
assert(ctx);
|
||||
assert(name);
|
||||
|
||||
node = ctx->device_map.root;
|
||||
while (node) {
|
||||
device = ratbagd_device_from_node(node);
|
||||
v = strcmp(name, device->name);
|
||||
if (!v)
|
||||
return device;
|
||||
else if (v < 0)
|
||||
node = node->left;
|
||||
else /* if (v > 0) */
|
||||
node = node->right;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ratbagd_device *ratbagd_device_first(struct ratbagd *ctx)
|
||||
{
|
||||
struct RBNode *node;
|
||||
|
||||
assert(ctx);
|
||||
|
||||
node = rbtree_first(&ctx->device_map);
|
||||
return node ? ratbagd_device_from_node(node) : NULL;
|
||||
}
|
||||
|
||||
struct ratbagd_device *ratbagd_device_next(struct ratbagd_device *device)
|
||||
{
|
||||
struct RBNode *node;
|
||||
|
||||
assert(device);
|
||||
|
||||
node = rbnode_next(&device->node);
|
||||
return node ? ratbagd_device_from_node(node) : NULL;
|
||||
}
|
||||
271
ratbagd/src/ratbagd-led.c
Normal file
271
ratbagd/src/ratbagd-led.c
Normal file
@@ -0,0 +1,271 @@
|
||||
/***
|
||||
This file is part of ratbagd.
|
||||
|
||||
Copyright 2017 Jente Hidskes <hjdskes@gmail.com>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the next
|
||||
paragraph) shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
***/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <libratbag.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
#include "ratbagd.h"
|
||||
#include "shared-macro.h"
|
||||
|
||||
struct ratbagd_led {
|
||||
struct ratbag_led *lib_led;
|
||||
unsigned int index;
|
||||
char *path;
|
||||
};
|
||||
|
||||
static int ratbagd_led_get_mode(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_led *led = userdata;
|
||||
enum ratbag_led_mode mode;
|
||||
|
||||
mode = ratbag_led_get_mode(led->lib_led);
|
||||
return sd_bus_message_append(reply, "u", mode);
|
||||
}
|
||||
|
||||
static int ratbagd_led_set_mode(sd_bus_message *m,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_led *led = userdata;
|
||||
enum ratbag_led_mode mode;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_read(m, "u", &mode);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ratbag_led_set_mode(led->lib_led, mode);
|
||||
return sd_bus_reply_method_return(m, "u", r);
|
||||
}
|
||||
|
||||
static int ratbagd_led_get_type(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_led *led = userdata;
|
||||
const char *type = NULL;
|
||||
enum ratbag_led_type t;
|
||||
|
||||
t = ratbag_led_get_type(led->lib_led);
|
||||
|
||||
switch(t) {
|
||||
default:
|
||||
log_error("Unknown led type %d\n", t);
|
||||
/* fallthrough */
|
||||
case RATBAG_LED_TYPE_UNKNOWN:
|
||||
type = "unknown";
|
||||
break;
|
||||
case RATBAG_LED_TYPE_LOGO:
|
||||
type = "logo";
|
||||
break;
|
||||
case RATBAG_LED_TYPE_SIDE:
|
||||
type = "side";
|
||||
break;
|
||||
}
|
||||
|
||||
return sd_bus_message_append(reply, "s", type);
|
||||
}
|
||||
|
||||
static int ratbagd_led_get_color(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_led *led = userdata;
|
||||
struct ratbag_color c;
|
||||
|
||||
c = ratbag_led_get_color(led->lib_led);
|
||||
return sd_bus_message_append(reply, "(uuu)", c.red, c.green, c.blue);
|
||||
}
|
||||
|
||||
static int ratbagd_led_set_color(sd_bus_message *m,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_led *led = userdata;
|
||||
struct ratbag_color c;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_read(m, "(uuu)", &c.red, &c.green, &c.blue);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ratbag_led_set_color(led->lib_led, c);
|
||||
return sd_bus_reply_method_return(m, "u", r);
|
||||
}
|
||||
|
||||
static int ratbagd_led_get_effect_rate(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_led *led = userdata;
|
||||
int rate;
|
||||
|
||||
rate = ratbag_led_get_effect_rate(led->lib_led);
|
||||
return sd_bus_message_append(reply, "i", rate);
|
||||
}
|
||||
|
||||
static int ratbagd_led_set_effect_rate(sd_bus_message *m,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_led *led = userdata;
|
||||
unsigned int rate;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_read(m, "u", &rate);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (rate < 100 || rate > 20000)
|
||||
return 0;
|
||||
|
||||
r = ratbag_led_set_effect_rate(led->lib_led, rate);
|
||||
return sd_bus_reply_method_return(m, "u", r);
|
||||
}
|
||||
|
||||
static int ratbagd_led_get_brightness(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_led *led = userdata;
|
||||
unsigned int brightness;
|
||||
|
||||
brightness = ratbag_led_get_brightness(led->lib_led);
|
||||
return sd_bus_message_append(reply, "u", brightness);
|
||||
}
|
||||
|
||||
static int ratbagd_led_set_brightness(sd_bus_message *m,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_led *led = userdata;
|
||||
unsigned int brightness;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_read(m, "u", &brightness);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (brightness > 255)
|
||||
return 0;
|
||||
|
||||
r = ratbag_led_set_brightness(led->lib_led, brightness);
|
||||
return sd_bus_reply_method_return(m, "u", r);
|
||||
}
|
||||
|
||||
const sd_bus_vtable ratbagd_led_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_PROPERTY("Index", "u", NULL, offsetof(struct ratbagd_led, index), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Mode", "u", ratbagd_led_get_mode, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Type", "s", ratbagd_led_get_type, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Color", "(uuu)", ratbagd_led_get_color, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("EffectRate", "i", ratbagd_led_get_effect_rate, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Brightness", "u", ratbagd_led_get_brightness, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
|
||||
SD_BUS_METHOD("SetMode", "u", "u", ratbagd_led_set_mode, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("SetColor", "(uuu)", "u", ratbagd_led_set_color, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("SetEffectRate", "u", "u", ratbagd_led_set_effect_rate, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("SetBrightness", "u", "u", ratbagd_led_set_brightness, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_VTABLE_END,
|
||||
};
|
||||
|
||||
int ratbagd_led_new(struct ratbagd_led **out,
|
||||
struct ratbagd_device *device,
|
||||
struct ratbagd_profile *profile,
|
||||
struct ratbag_led *lib_led,
|
||||
unsigned int index)
|
||||
{
|
||||
_cleanup_(ratbagd_led_freep) struct ratbagd_led *led = NULL;
|
||||
char profile_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1],
|
||||
led_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1];
|
||||
int r;
|
||||
|
||||
assert(out);
|
||||
assert(lib_led);
|
||||
|
||||
led = calloc(1, sizeof(*led));
|
||||
if (!led)
|
||||
return -ENOMEM;
|
||||
|
||||
led->lib_led = lib_led;
|
||||
led->index = index;
|
||||
|
||||
sprintf(profile_buffer, "p%u", ratbagd_profile_get_index(profile));
|
||||
sprintf(led_buffer, "b%u", index);
|
||||
r = sd_bus_path_encode_many(&led->path,
|
||||
"/org/freedesktop/ratbag1/led/%/%/%",
|
||||
ratbagd_device_get_name(device),
|
||||
profile_buffer,
|
||||
led_buffer);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*out = led;
|
||||
led = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *ratbagd_led_get_path(struct ratbagd_led *led)
|
||||
{
|
||||
assert(led);
|
||||
return led->path;
|
||||
}
|
||||
|
||||
struct ratbagd_led *ratbagd_led_free(struct ratbagd_led *led)
|
||||
{
|
||||
if (!led)
|
||||
return NULL;
|
||||
|
||||
led->path = mfree(led->path);
|
||||
led->lib_led = ratbag_led_unref(led->lib_led);
|
||||
|
||||
return mfree(led);
|
||||
}
|
||||
|
||||
740
ratbagd/src/ratbagd-profile.c
Normal file
740
ratbagd/src/ratbagd-profile.c
Normal file
@@ -0,0 +1,740 @@
|
||||
/***
|
||||
This file is part of ratbagd.
|
||||
|
||||
Copyright 2015 David Herrmann <dh.herrmann@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the next
|
||||
paragraph) shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
***/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <libratbag.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <systemd/sd-event.h>
|
||||
#include "ratbagd.h"
|
||||
#include "shared-macro.h"
|
||||
|
||||
struct ratbagd_profile {
|
||||
struct ratbagd_device *device;
|
||||
struct ratbag_profile *lib_profile;
|
||||
unsigned int index;
|
||||
char *path;
|
||||
|
||||
sd_bus_slot *resolution_vtable_slot;
|
||||
sd_bus_slot *resolution_enum_slot;
|
||||
unsigned int n_resolutions;
|
||||
struct ratbagd_resolution **resolutions;
|
||||
|
||||
sd_bus_slot *button_vtable_slot;
|
||||
sd_bus_slot *button_enum_slot;
|
||||
unsigned int n_buttons;
|
||||
struct ratbagd_button **buttons;
|
||||
|
||||
sd_bus_slot *led_vtable_slot;
|
||||
sd_bus_slot *led_enum_slot;
|
||||
unsigned int n_leds;
|
||||
struct ratbagd_led **leds;
|
||||
};
|
||||
|
||||
static int ratbagd_profile_find_resolution(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
void *userdata,
|
||||
void **found,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
_cleanup_(freep) char *name = NULL;
|
||||
struct ratbagd_profile *profile = userdata;
|
||||
unsigned int index;
|
||||
int r;
|
||||
|
||||
r = sd_bus_path_decode_many(path,
|
||||
"/org/freedesktop/ratbag1/resolution/%/p%/r%",
|
||||
NULL,
|
||||
NULL,
|
||||
&name);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = safe_atou(name, &index);
|
||||
if (r < 0)
|
||||
return 0;
|
||||
|
||||
if (index >= profile->n_resolutions || !profile->resolutions[index])
|
||||
return 0;
|
||||
|
||||
*found = profile->resolutions[index];
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ratbagd_profile_get_resolutions(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_profile *profile = userdata;
|
||||
struct ratbagd_resolution *resolution;
|
||||
unsigned int i;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "o");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (i = 0; i < profile->n_resolutions; ++i) {
|
||||
resolution = profile->resolutions[i];
|
||||
if (!resolution)
|
||||
continue;
|
||||
|
||||
r = sd_bus_message_append(reply,
|
||||
"o",
|
||||
ratbagd_resolution_get_path(resolution));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static int ratbagd_profile_get_buttons(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_profile *profile = userdata;
|
||||
struct ratbagd_button *button;
|
||||
unsigned int i;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "o");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (i = 0; i < profile->n_buttons; ++i) {
|
||||
button = profile->buttons[i];
|
||||
if (!button)
|
||||
continue;
|
||||
|
||||
r = sd_bus_message_append(reply,
|
||||
"o",
|
||||
ratbagd_button_get_path(button));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static int ratbagd_profile_get_leds(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_profile *profile = userdata;
|
||||
struct ratbagd_led *led;
|
||||
unsigned int i;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "o");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (i = 0; i < profile->n_leds; ++i) {
|
||||
led = profile->leds[i];
|
||||
if (!led)
|
||||
continue;
|
||||
|
||||
r = sd_bus_message_append(reply,
|
||||
"o",
|
||||
ratbagd_led_get_path(led));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static int ratbagd_profile_get_active_resolution(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_profile *profile = userdata;
|
||||
struct ratbagd_resolution *resolution;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < profile->n_resolutions; ++i) {
|
||||
resolution = profile->resolutions[i];
|
||||
if (!resolution)
|
||||
continue;
|
||||
if (!ratbagd_resolution_is_active(resolution))
|
||||
continue;
|
||||
|
||||
return sd_bus_message_append(reply, "u", i);
|
||||
}
|
||||
|
||||
log_error("Unable to find active resolution for %s\n",
|
||||
ratbagd_device_get_name(profile->device));
|
||||
return sd_bus_message_append(reply, "u", 0);
|
||||
}
|
||||
|
||||
static int ratbagd_profile_get_default_resolution(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_profile *profile = userdata;
|
||||
struct ratbagd_resolution *resolution;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < profile->n_resolutions; ++i) {
|
||||
resolution = profile->resolutions[i];
|
||||
if (!resolution)
|
||||
continue;
|
||||
if (!ratbagd_resolution_is_default(resolution))
|
||||
continue;
|
||||
|
||||
return sd_bus_message_append(reply, "u", i);
|
||||
}
|
||||
|
||||
log_error("Unable to find default resolution for %s\n",
|
||||
ratbagd_device_get_name(profile->device));
|
||||
return sd_bus_message_append(reply, "u", 0);
|
||||
}
|
||||
|
||||
static int ratbagd_profile_find_button(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
void *userdata,
|
||||
void **found,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
_cleanup_(freep) char *name = NULL;
|
||||
struct ratbagd_profile *profile = userdata;
|
||||
unsigned int index;
|
||||
int r;
|
||||
|
||||
r = sd_bus_path_decode_many(path,
|
||||
"/org/freedesktop/ratbag1/button/%/p%/b%",
|
||||
NULL,
|
||||
NULL,
|
||||
&name);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = safe_atou(name, &index);
|
||||
if (r < 0)
|
||||
return 0;
|
||||
|
||||
if (index >= profile->n_buttons || !profile->buttons[index])
|
||||
return 0;
|
||||
|
||||
*found = profile->buttons[index];
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ratbagd_profile_find_led(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
void *userdata,
|
||||
void **found,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
_cleanup_(freep) char *name = NULL;
|
||||
struct ratbagd_profile *profile = userdata;
|
||||
unsigned int index;
|
||||
int r;
|
||||
|
||||
r = sd_bus_path_decode_many(path,
|
||||
"/org/freedesktop/ratbag1/led/%/p%/b%",
|
||||
NULL,
|
||||
NULL,
|
||||
&name);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = safe_atou(name, &index);
|
||||
if (r < 0)
|
||||
return 0;
|
||||
|
||||
if (index >= profile->n_leds || !profile->leds[index])
|
||||
return 0;
|
||||
|
||||
*found = profile->leds[index];
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ratbagd_profile_set_active(sd_bus_message *m,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_profile *profile = userdata;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_read(m, "");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) sd_bus_emit_signal(sd_bus_message_get_bus(m),
|
||||
"/org/freedesktop/ratbag1",
|
||||
"/org.freedesktop.ratbag1.Profile",
|
||||
"ActiveProfileChanged",
|
||||
"u",
|
||||
profile->index);
|
||||
|
||||
return sd_bus_reply_method_return(m, "u",
|
||||
ratbag_profile_set_active(profile->lib_profile));
|
||||
}
|
||||
|
||||
static int ratbagd_profile_get_resolution_by_index(sd_bus_message *m,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_profile *profile = userdata;
|
||||
struct ratbagd_resolution *resolution;
|
||||
unsigned int index;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_read(m, "u", &index);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (index >= profile->n_resolutions || !profile->resolutions[index])
|
||||
return 0;
|
||||
|
||||
resolution = profile->resolutions[index];
|
||||
return sd_bus_reply_method_return(m, "o",
|
||||
ratbagd_resolution_get_path(resolution));
|
||||
}
|
||||
|
||||
const sd_bus_vtable ratbagd_profile_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_PROPERTY("Index", "u", NULL, offsetof(struct ratbagd_profile, index), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Resolutions", "ao", ratbagd_profile_get_resolutions, 0, 0),
|
||||
SD_BUS_PROPERTY("Buttons", "ao", ratbagd_profile_get_buttons, 0, 0),
|
||||
SD_BUS_PROPERTY("Leds", "ao", ratbagd_profile_get_leds, 0, 0),
|
||||
SD_BUS_PROPERTY("ActiveResolution", "u", ratbagd_profile_get_active_resolution, 0, 0),
|
||||
SD_BUS_PROPERTY("DefaultResolution", "u", ratbagd_profile_get_default_resolution, 0, 0),
|
||||
SD_BUS_METHOD("SetActive", "", "u", ratbagd_profile_set_active, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("GetResolutionByIndex", "u", "o", ratbagd_profile_get_resolution_by_index, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_SIGNAL("ActiveProfileChanged", "u", 0),
|
||||
SD_BUS_VTABLE_END,
|
||||
};
|
||||
|
||||
int ratbagd_profile_new(struct ratbagd_profile **out,
|
||||
struct ratbagd_device *device,
|
||||
struct ratbag_profile *lib_profile,
|
||||
unsigned int index)
|
||||
{
|
||||
_cleanup_(ratbagd_profile_freep) struct ratbagd_profile *profile = NULL;
|
||||
struct ratbag_resolution *resolution;
|
||||
struct ratbag_button *button;
|
||||
struct ratbag_led *led;
|
||||
char index_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1];
|
||||
unsigned int i;
|
||||
int r;
|
||||
|
||||
assert(out);
|
||||
assert(lib_profile);
|
||||
|
||||
profile = calloc(1, sizeof(*profile));
|
||||
if (!profile)
|
||||
return -ENOMEM;
|
||||
|
||||
profile->device = device;
|
||||
profile->lib_profile = lib_profile;
|
||||
profile->index = index;
|
||||
|
||||
sprintf(index_buffer, "p%u", index);
|
||||
r = sd_bus_path_encode_many(&profile->path,
|
||||
"/org/freedesktop/ratbag1/profile/%/%",
|
||||
ratbagd_device_get_name(device),
|
||||
index_buffer);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
profile->n_resolutions = ratbag_profile_get_num_resolutions(profile->lib_profile);
|
||||
profile->resolutions = calloc(profile->n_resolutions, sizeof(*profile->resolutions));
|
||||
if (!profile->resolutions)
|
||||
return -ENOMEM;
|
||||
|
||||
profile->n_buttons = ratbagd_device_get_num_buttons(device);
|
||||
profile->buttons = calloc(profile->n_buttons, sizeof(*profile->buttons));
|
||||
if (!profile->buttons)
|
||||
return -ENOMEM;
|
||||
|
||||
profile->n_leds = ratbagd_device_get_num_leds(device);
|
||||
profile->leds = calloc(profile->n_leds, sizeof(*profile->leds));
|
||||
if (!profile->leds)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < profile->n_resolutions; ++i) {
|
||||
resolution = ratbag_profile_get_resolution(profile->lib_profile, i);
|
||||
if (!resolution)
|
||||
continue;
|
||||
|
||||
r = ratbagd_resolution_new(&profile->resolutions[i],
|
||||
device,
|
||||
profile,
|
||||
resolution,
|
||||
i);
|
||||
if (r < 0) {
|
||||
errno = -r;
|
||||
log_error("Cannot allocate resolution for '%s': %m\n",
|
||||
ratbagd_device_get_name(device));
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < profile->n_buttons; ++i) {
|
||||
button = ratbag_profile_get_button(profile->lib_profile, i);
|
||||
if (!button)
|
||||
continue;
|
||||
|
||||
r = ratbagd_button_new(&profile->buttons[i],
|
||||
device,
|
||||
profile,
|
||||
button,
|
||||
i);
|
||||
if (r < 0) {
|
||||
errno = -r;
|
||||
log_error("Cannot allocate button for '%s': %m\n",
|
||||
ratbagd_device_get_name(device));
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < profile->n_leds; ++i) {
|
||||
led = ratbag_profile_get_led(profile->lib_profile, i);
|
||||
if (!led)
|
||||
continue;
|
||||
|
||||
r = ratbagd_led_new(&profile->leds[i],
|
||||
device,
|
||||
profile,
|
||||
led,
|
||||
i);
|
||||
if (r < 0) {
|
||||
errno = -r;
|
||||
log_error("Cannot allocate led for '%s': %m\n",
|
||||
ratbagd_device_get_name(device));
|
||||
}
|
||||
}
|
||||
|
||||
*out = profile;
|
||||
profile = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ratbagd_profile *ratbagd_profile_free(struct ratbagd_profile *profile)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (!profile)
|
||||
return NULL;
|
||||
|
||||
profile->resolution_vtable_slot = sd_bus_slot_unref(profile->resolution_vtable_slot);
|
||||
profile->resolution_enum_slot = sd_bus_slot_unref(profile->resolution_enum_slot);
|
||||
profile->button_vtable_slot = sd_bus_slot_unref(profile->button_vtable_slot);
|
||||
profile->button_enum_slot = sd_bus_slot_unref(profile->button_enum_slot);
|
||||
profile->led_vtable_slot = sd_bus_slot_unref(profile->led_vtable_slot);
|
||||
profile->led_enum_slot = sd_bus_slot_unref(profile->led_enum_slot);
|
||||
|
||||
for (i = 0; i< profile->n_leds; ++i)
|
||||
ratbagd_led_free(profile->leds[i]);
|
||||
for (i = 0; i< profile->n_buttons; ++i)
|
||||
ratbagd_button_free(profile->buttons[i]);
|
||||
for (i = 0; i< profile->n_resolutions; ++i)
|
||||
ratbagd_resolution_free(profile->resolutions[i]);
|
||||
|
||||
mfree(profile->leds);
|
||||
mfree(profile->buttons);
|
||||
mfree(profile->resolutions);
|
||||
|
||||
profile->path = mfree(profile->path);
|
||||
profile->lib_profile = ratbag_profile_unref(profile->lib_profile);
|
||||
|
||||
return mfree(profile);
|
||||
}
|
||||
|
||||
const char *ratbagd_profile_get_path(struct ratbagd_profile *profile)
|
||||
{
|
||||
assert(profile);
|
||||
return profile->path;
|
||||
}
|
||||
|
||||
bool ratbagd_profile_is_active(struct ratbagd_profile *profile)
|
||||
{
|
||||
assert(profile);
|
||||
return ratbag_profile_is_active(profile->lib_profile) != 0;
|
||||
}
|
||||
|
||||
unsigned int ratbagd_profile_get_index(struct ratbagd_profile *profile)
|
||||
{
|
||||
assert(profile);
|
||||
return profile->index;
|
||||
}
|
||||
|
||||
static int ratbagd_profile_list_resolutions(sd_bus *bus,
|
||||
const char *path,
|
||||
void *userdata,
|
||||
char ***paths,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_profile *profile = userdata;
|
||||
struct ratbagd_resolution *resolution;
|
||||
char **resolutions;
|
||||
unsigned int i;
|
||||
|
||||
resolutions = calloc(profile->n_resolutions + 1, sizeof(char *));
|
||||
if (!resolutions)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < profile->n_resolutions; ++i) {
|
||||
resolution = profile->resolutions[i];
|
||||
if (!resolution)
|
||||
continue;
|
||||
|
||||
resolutions[i] = strdup(ratbagd_resolution_get_path(resolution));
|
||||
if (!resolutions[i])
|
||||
goto error;
|
||||
}
|
||||
|
||||
resolutions[i] = NULL;
|
||||
*paths = resolutions;
|
||||
return 1;
|
||||
|
||||
error:
|
||||
for (i = 0; resolutions[i]; ++i)
|
||||
free(resolutions[i]);
|
||||
free(resolutions);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
||||
int ratbagd_profile_register_resolutions(struct sd_bus *bus,
|
||||
struct ratbagd_device *device,
|
||||
struct ratbagd_profile *profile)
|
||||
{
|
||||
_cleanup_(freep) char *prefix = NULL;
|
||||
char index_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1];
|
||||
int r;
|
||||
|
||||
sprintf(index_buffer, "p%u", profile->index);
|
||||
|
||||
/* register resolution interfaces */
|
||||
r = sd_bus_path_encode_many(&prefix,
|
||||
"/org/freedesktop/ratbag1/resolution/%/%",
|
||||
ratbagd_device_get_name(device),
|
||||
index_buffer);
|
||||
|
||||
if (r >= 0) {
|
||||
r = sd_bus_add_fallback_vtable(bus,
|
||||
&profile->resolution_vtable_slot,
|
||||
prefix,
|
||||
"org.freedesktop.ratbag1.Resolution",
|
||||
ratbagd_resolution_vtable,
|
||||
ratbagd_profile_find_resolution,
|
||||
profile);
|
||||
if (r >= 0)
|
||||
r = sd_bus_add_node_enumerator(bus,
|
||||
&profile->resolution_enum_slot,
|
||||
prefix,
|
||||
ratbagd_profile_list_resolutions,
|
||||
profile);
|
||||
}
|
||||
if (r < 0) {
|
||||
errno = -r;
|
||||
log_error("Cannot register resolutions for '%s': %m\n",
|
||||
ratbagd_device_get_name(device));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ratbagd_profile_list_buttons(sd_bus *bus,
|
||||
const char *path,
|
||||
void *userdata,
|
||||
char ***paths,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_profile *profile = userdata;
|
||||
struct ratbagd_button *button;
|
||||
char **buttons;
|
||||
unsigned int i;
|
||||
|
||||
buttons = calloc(profile->n_buttons + 1, sizeof(char *));
|
||||
if (!buttons)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < profile->n_buttons; ++i) {
|
||||
button = profile->buttons[i];
|
||||
if (!button)
|
||||
continue;
|
||||
|
||||
buttons[i] = strdup(ratbagd_button_get_path(button));
|
||||
if (!buttons[i])
|
||||
goto error;
|
||||
}
|
||||
|
||||
buttons[i] = NULL;
|
||||
*paths = buttons;
|
||||
return 1;
|
||||
|
||||
error:
|
||||
for (i = 0; buttons[i]; ++i)
|
||||
free(buttons[i]);
|
||||
free(buttons);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int ratbagd_profile_register_buttons(struct sd_bus *bus,
|
||||
struct ratbagd_device *device,
|
||||
struct ratbagd_profile *profile)
|
||||
{
|
||||
_cleanup_(freep) char *prefix = NULL;
|
||||
char index_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1];
|
||||
int r;
|
||||
|
||||
sprintf(index_buffer, "p%u", profile->index);
|
||||
|
||||
/* register button interfaces */
|
||||
r = sd_bus_path_encode_many(&prefix,
|
||||
"/org/freedesktop/ratbag1/button/%/%",
|
||||
ratbagd_device_get_name(device),
|
||||
index_buffer);
|
||||
|
||||
if (r >= 0) {
|
||||
r = sd_bus_add_fallback_vtable(bus,
|
||||
&profile->button_vtable_slot,
|
||||
prefix,
|
||||
"org.freedesktop.ratbag1.Button",
|
||||
ratbagd_button_vtable,
|
||||
ratbagd_profile_find_button,
|
||||
profile);
|
||||
if (r >= 0)
|
||||
r = sd_bus_add_node_enumerator(bus,
|
||||
&profile->button_enum_slot,
|
||||
prefix,
|
||||
ratbagd_profile_list_buttons,
|
||||
profile);
|
||||
}
|
||||
if (r < 0) {
|
||||
errno = -r;
|
||||
log_error("Cannot register buttons for '%s': %m\n",
|
||||
ratbagd_device_get_name(device));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ratbagd_profile_list_leds(sd_bus *bus,
|
||||
const char *path,
|
||||
void *userdata,
|
||||
char ***paths,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_profile *profile = userdata;
|
||||
struct ratbagd_led *led;
|
||||
char **leds;
|
||||
unsigned int i;
|
||||
|
||||
leds = calloc(profile->n_leds + 1, sizeof(char *));
|
||||
if (!leds)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < profile->n_leds; ++i) {
|
||||
led = profile->leds[i];
|
||||
if (!led)
|
||||
continue;
|
||||
|
||||
leds[i] = strdup(ratbagd_led_get_path(led));
|
||||
if (!leds[i])
|
||||
goto error;
|
||||
}
|
||||
|
||||
leds[i] = NULL;
|
||||
*paths = leds;
|
||||
return 1;
|
||||
|
||||
error:
|
||||
for (i = 0; leds[i]; ++i)
|
||||
free(leds[i]);
|
||||
free(leds);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int ratbagd_profile_register_leds(struct sd_bus *bus,
|
||||
struct ratbagd_device *device,
|
||||
struct ratbagd_profile *profile)
|
||||
{
|
||||
_cleanup_(freep) char *prefix = NULL;
|
||||
char index_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1];
|
||||
int r;
|
||||
|
||||
sprintf(index_buffer, "p%u", profile->index);
|
||||
|
||||
/* register led interfaces */
|
||||
r = sd_bus_path_encode_many(&prefix,
|
||||
"/org/freedesktop/ratbag1/led/%/%",
|
||||
ratbagd_device_get_name(device),
|
||||
index_buffer);
|
||||
|
||||
if (r >= 0) {
|
||||
r = sd_bus_add_fallback_vtable(bus,
|
||||
&profile->led_vtable_slot,
|
||||
prefix,
|
||||
"org.freedesktop.ratbag1.Led",
|
||||
ratbagd_led_vtable,
|
||||
ratbagd_profile_find_led,
|
||||
profile);
|
||||
if (r >= 0)
|
||||
r = sd_bus_add_node_enumerator(bus,
|
||||
&profile->led_enum_slot,
|
||||
prefix,
|
||||
ratbagd_profile_list_leds,
|
||||
profile);
|
||||
}
|
||||
if (r < 0) {
|
||||
errno = -r;
|
||||
log_error("Cannot register leds for '%s': %m\n",
|
||||
ratbagd_device_get_name(device));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
235
ratbagd/src/ratbagd-resolution.c
Normal file
235
ratbagd/src/ratbagd-resolution.c
Normal file
@@ -0,0 +1,235 @@
|
||||
/***
|
||||
This file is part of ratbagd.
|
||||
|
||||
Copyright 2016 Red Hat, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the next
|
||||
paragraph) shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
***/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <libratbag.h>
|
||||
#include <libudev.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <systemd/sd-event.h>
|
||||
#include "ratbagd.h"
|
||||
#include "shared-macro.h"
|
||||
|
||||
struct ratbagd_resolution {
|
||||
struct ratbag_resolution *lib_resolution;
|
||||
unsigned int index;
|
||||
char *path;
|
||||
unsigned int xres, yres;
|
||||
unsigned int rate;
|
||||
};
|
||||
|
||||
static int ratbagd_resolution_set_report_rate(sd_bus_message *m,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_resolution *resolution = userdata;
|
||||
unsigned int rate;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_read(m, "u", &rate);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* basic sanity check */
|
||||
if (rate > 5000 || rate % 100)
|
||||
return 0;
|
||||
|
||||
r = ratbag_resolution_set_report_rate(resolution->lib_resolution,
|
||||
rate);
|
||||
if (r == 0) {
|
||||
resolution->rate = rate;
|
||||
}
|
||||
return sd_bus_reply_method_return(m, "u", r);
|
||||
}
|
||||
|
||||
static int ratbagd_resolution_set_resolution(sd_bus_message *m,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_resolution *resolution = userdata;
|
||||
unsigned int xres, yres;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_read(m, "uu", &xres, &yres);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ratbag_resolution_set_dpi_xy(resolution->lib_resolution,
|
||||
xres, yres);
|
||||
if (r == 0) {
|
||||
resolution->xres = xres;
|
||||
resolution->yres = yres;
|
||||
}
|
||||
|
||||
(void) sd_bus_emit_signal(sd_bus_message_get_bus(m),
|
||||
"/org/freedesktop/ratbag1",
|
||||
"/org.freedesktop.ratbag1.Resolution",
|
||||
"ActiveResolutionChanged",
|
||||
"u",
|
||||
resolution->index);
|
||||
|
||||
return sd_bus_reply_method_return(m, "u", r);
|
||||
}
|
||||
|
||||
static int ratbagd_resolution_set_default(sd_bus_message *m,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_resolution *resolution = userdata;
|
||||
int r;
|
||||
|
||||
r = ratbag_resolution_set_default(resolution->lib_resolution);
|
||||
|
||||
(void) sd_bus_emit_signal(sd_bus_message_get_bus(m),
|
||||
"/org/freedesktop/ratbag1",
|
||||
"/org.freedesktop.ratbag1.Resolution",
|
||||
"DefaultResolutionChanged",
|
||||
"u",
|
||||
resolution->index);
|
||||
|
||||
return sd_bus_reply_method_return(m, "u", r);
|
||||
}
|
||||
|
||||
static int
|
||||
ratbagd_resolution_get_capabilities(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_resolution *resolution = userdata;
|
||||
struct ratbag_resolution *lib_resolution = resolution->lib_resolution;
|
||||
enum ratbag_device_capability cap;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "u");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
cap = RATBAG_RESOLUTION_CAP_INDIVIDUAL_REPORT_RATE;
|
||||
if (ratbag_resolution_has_capability(lib_resolution, cap)) {
|
||||
r = sd_bus_message_append(reply, "u", cap);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
cap = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION;
|
||||
if (ratbag_resolution_has_capability(lib_resolution, cap)) {
|
||||
r = sd_bus_message_append(reply, "u", cap);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
const sd_bus_vtable ratbagd_resolution_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_PROPERTY("Index", "u", NULL, offsetof(struct ratbagd_resolution, index), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Capabilities", "au", ratbagd_resolution_get_capabilities, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("XResolution", "u", NULL, offsetof(struct ratbagd_resolution, xres), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("YResolution", "u", NULL, offsetof(struct ratbagd_resolution, yres), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("ReportRate", "u", NULL, offsetof(struct ratbagd_resolution, rate), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_METHOD("SetReportRate", "u", "u", ratbagd_resolution_set_report_rate, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("SetResolution", "uu", "u", ratbagd_resolution_set_resolution, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("SetDefault", "", "u", ratbagd_resolution_set_default, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_SIGNAL("ActiveResolutionChanged", "u", 0),
|
||||
SD_BUS_SIGNAL("DefaultResolutionChanged", "u", 0),
|
||||
SD_BUS_VTABLE_END,
|
||||
};
|
||||
|
||||
int ratbagd_resolution_new(struct ratbagd_resolution **out,
|
||||
struct ratbagd_device *device,
|
||||
struct ratbagd_profile *profile,
|
||||
struct ratbag_resolution *lib_resolution,
|
||||
unsigned int index)
|
||||
{
|
||||
_cleanup_(ratbagd_resolution_freep) struct ratbagd_resolution *resolution = NULL;
|
||||
char profile_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1],
|
||||
resolution_buffer[DECIMAL_TOKEN_MAX(unsigned int) + 1];
|
||||
int r;
|
||||
|
||||
assert(out);
|
||||
assert(lib_resolution);
|
||||
|
||||
resolution = calloc(1, sizeof(*resolution));
|
||||
if (!resolution)
|
||||
return -ENOMEM;
|
||||
|
||||
resolution->lib_resolution = lib_resolution;
|
||||
resolution->index = index;
|
||||
resolution->xres = ratbag_resolution_get_dpi_x(lib_resolution);
|
||||
resolution->yres = ratbag_resolution_get_dpi_y(lib_resolution);
|
||||
resolution->rate = ratbag_resolution_get_report_rate(lib_resolution);
|
||||
|
||||
sprintf(profile_buffer, "p%u", ratbagd_profile_get_index(profile));
|
||||
sprintf(resolution_buffer, "r%u", index);
|
||||
r = sd_bus_path_encode_many(&resolution->path,
|
||||
"/org/freedesktop/ratbag1/resolution/%/%/%",
|
||||
ratbagd_device_get_name(device),
|
||||
profile_buffer,
|
||||
resolution_buffer);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*out = resolution;
|
||||
resolution = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *ratbagd_resolution_get_path(struct ratbagd_resolution *resolution)
|
||||
{
|
||||
assert(resolution);
|
||||
return resolution->path;
|
||||
}
|
||||
|
||||
struct ratbagd_resolution *ratbagd_resolution_free(struct ratbagd_resolution *resolution)
|
||||
{
|
||||
if (!resolution)
|
||||
return NULL;
|
||||
|
||||
resolution->path = mfree(resolution->path);
|
||||
resolution->lib_resolution = ratbag_resolution_unref(resolution->lib_resolution);
|
||||
|
||||
return mfree(resolution);
|
||||
}
|
||||
|
||||
bool ratbagd_resolution_is_active(struct ratbagd_resolution *resolution)
|
||||
{
|
||||
assert(resolution);
|
||||
return ratbag_resolution_is_active(resolution->lib_resolution) != 0;
|
||||
}
|
||||
|
||||
bool ratbagd_resolution_is_default(struct ratbagd_resolution *resolution)
|
||||
{
|
||||
assert(resolution);
|
||||
return ratbag_resolution_is_default(resolution->lib_resolution) != 0;
|
||||
}
|
||||
489
ratbagd/src/ratbagd.c
Normal file
489
ratbagd/src/ratbagd.c
Normal file
@@ -0,0 +1,489 @@
|
||||
/***
|
||||
This file is part of ratbagd.
|
||||
|
||||
Copyright 2015 David Herrmann <dh.herrmann@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the next
|
||||
paragraph) shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
***/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <libratbag.h>
|
||||
#include <libudev.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <systemd/sd-event.h>
|
||||
#include "ratbagd.h"
|
||||
#include "shared-macro.h"
|
||||
|
||||
static bool verbose = false;
|
||||
|
||||
void log_verbose(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
|
||||
if (verbose)
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void log_error(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
fprintf(stderr, "%s error: ", program_invocation_short_name);
|
||||
vfprintf(stderr, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static int ratbagd_find_device(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
void *userdata,
|
||||
void **found,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
_cleanup_(freep) char *name = NULL;
|
||||
struct ratbagd *ctx = userdata;
|
||||
struct ratbagd_device *device;
|
||||
int r;
|
||||
|
||||
r = sd_bus_path_decode_many(path,
|
||||
"/org/freedesktop/ratbag1/device/%",
|
||||
&name);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
device = ratbagd_device_lookup(ctx, name);
|
||||
if (!device)
|
||||
return 0;
|
||||
|
||||
*found = device;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ratbagd_list_devices(sd_bus *bus,
|
||||
const char *path,
|
||||
void *userdata,
|
||||
char ***paths,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd *ctx = userdata;
|
||||
struct ratbagd_device *device;
|
||||
char **devices, **pos;
|
||||
|
||||
devices = calloc(ctx->n_devices + 1, sizeof(char *));
|
||||
if (!devices)
|
||||
return -ENOMEM;
|
||||
|
||||
pos = devices;
|
||||
|
||||
RATBAGD_DEVICE_FOREACH(device, ctx) {
|
||||
*pos = strdup(ratbagd_device_get_path(device));
|
||||
if (!*pos)
|
||||
goto error;
|
||||
++pos;
|
||||
}
|
||||
|
||||
*pos = NULL;
|
||||
*paths = devices;
|
||||
return 1;
|
||||
|
||||
error:
|
||||
for (pos = devices; *pos; ++pos)
|
||||
free(*pos);
|
||||
free(devices);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int ratbagd_get_devices(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd *ctx = userdata;
|
||||
struct ratbagd_device *device;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "o");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
RATBAGD_DEVICE_FOREACH(device, ctx) {
|
||||
r = sd_bus_message_append(reply,
|
||||
"o",
|
||||
ratbagd_device_get_path(device));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static const sd_bus_vtable ratbagd_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_PROPERTY("Devices", "ao", ratbagd_get_devices, 0, 0),
|
||||
SD_BUS_SIGNAL("DeviceNew", "o", 0),
|
||||
SD_BUS_SIGNAL("DeviceRemoved", "o", 0),
|
||||
SD_BUS_VTABLE_END,
|
||||
};
|
||||
|
||||
static void ratbagd_process_device(struct ratbagd *ctx,
|
||||
struct udev_device *udevice)
|
||||
{
|
||||
struct ratbag_device *lib_device;
|
||||
struct ratbagd_device *device;
|
||||
const char *name;
|
||||
int r;
|
||||
|
||||
/*
|
||||
* TODO: libratbag should provide some mechanism to allow
|
||||
* device-grouping, just like libinput does. If multiple input
|
||||
* devices belong to the same virtual device, we should not add
|
||||
* it multiple times. Instead, libratbag should group them and
|
||||
* provide *us* a unique name that identifies the group, rather
|
||||
* than taking a random input-device as tag.
|
||||
*/
|
||||
|
||||
name = udev_device_get_sysname(udevice);
|
||||
if (!name || !startswith(name, "event"))
|
||||
return;
|
||||
|
||||
device = ratbagd_device_lookup(ctx, name);
|
||||
|
||||
if (streq_ptr("remove", udev_device_get_action(udevice))) {
|
||||
/* device was removed, unlink it and destroy our context */
|
||||
if (device) {
|
||||
(void) sd_bus_emit_signal(ctx->bus,
|
||||
"/org/freedesktop/ratbag1",
|
||||
"org.freedesktop.ratbag1.Manager",
|
||||
"DeviceRemoved",
|
||||
"o",
|
||||
ratbagd_device_get_path(device));
|
||||
ratbagd_device_unlink(device);
|
||||
ratbagd_device_free(device);
|
||||
}
|
||||
} else if (device) {
|
||||
/* device already known, refresh our view of the device */
|
||||
} else {
|
||||
enum ratbag_error_code error;
|
||||
|
||||
/* device unknown, create new one and link it */
|
||||
error = ratbag_device_new_from_udev_device(ctx->lib_ctx,
|
||||
udevice,
|
||||
&lib_device);
|
||||
if (error != RATBAG_SUCCESS)
|
||||
return; /* unsupported device */
|
||||
|
||||
r = ratbagd_device_new(&device, ctx, name, lib_device);
|
||||
|
||||
/* the ratbagd_device takes its own reference, drop ours */
|
||||
ratbag_device_unref(lib_device);
|
||||
|
||||
if (r < 0) {
|
||||
log_error("Cannot track device '%s'\n", name);
|
||||
return;
|
||||
}
|
||||
|
||||
ratbagd_device_link(device);
|
||||
(void) sd_bus_emit_signal(ctx->bus,
|
||||
"/org/freedesktop/ratbag1",
|
||||
"org.freedesktop.ratbag1.Manager",
|
||||
"DeviceNew",
|
||||
"o",
|
||||
ratbagd_device_get_path(device));
|
||||
}
|
||||
}
|
||||
|
||||
static int ratbagd_monitor_event(sd_event_source *source,
|
||||
int fd,
|
||||
uint32_t mask,
|
||||
void *userdata)
|
||||
{
|
||||
struct ratbagd *ctx = userdata;
|
||||
struct udev_device *udevice;
|
||||
|
||||
udevice = udev_monitor_receive_device(ctx->monitor);
|
||||
if (!udevice)
|
||||
return 0;
|
||||
|
||||
ratbagd_process_device(ctx, udevice);
|
||||
udev_device_unref(udevice);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ratbagd_lib_open_restricted(const char *path,
|
||||
int flags,
|
||||
void *userdata)
|
||||
{
|
||||
return open(path, flags, 0);
|
||||
}
|
||||
|
||||
static void ratbagd_lib_close_restricted(int fd, void *userdata)
|
||||
{
|
||||
safe_close(fd);
|
||||
}
|
||||
|
||||
static const struct ratbag_interface ratbagd_lib_interface = {
|
||||
.open_restricted = ratbagd_lib_open_restricted,
|
||||
.close_restricted = ratbagd_lib_close_restricted,
|
||||
};
|
||||
|
||||
static struct ratbagd *ratbagd_free(struct ratbagd *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
return NULL;
|
||||
|
||||
ctx->bus = sd_bus_flush_close_unref(ctx->bus);
|
||||
ctx->monitor_source = sd_event_source_unref(ctx->monitor_source);
|
||||
ctx->monitor = udev_monitor_unref(ctx->monitor);
|
||||
ctx->lib_ctx = ratbag_unref(ctx->lib_ctx);
|
||||
ctx->event = sd_event_unref(ctx->event);
|
||||
|
||||
assert(!ctx->device_map.root);
|
||||
assert(!ctx->lib_ctx); /* ratbag returns non-NULL if still pinned */
|
||||
|
||||
return mfree(ctx);
|
||||
}
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd *, ratbagd_free);
|
||||
|
||||
static int ratbagd_init_monitor(struct ratbagd *ctx)
|
||||
{
|
||||
struct udev *udev;
|
||||
int r;
|
||||
|
||||
udev = udev_new();
|
||||
if (!udev)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->monitor = udev_monitor_new_from_netlink(udev, "udev");
|
||||
|
||||
/* we don't need the context to stay around; drop it */
|
||||
udev_unref(udev);
|
||||
|
||||
if (!ctx->monitor)
|
||||
return -ENOMEM;
|
||||
|
||||
r = udev_monitor_filter_add_match_subsystem_devtype(ctx->monitor,
|
||||
"input",
|
||||
NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = udev_monitor_enable_receiving(ctx->monitor);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_event_add_io(ctx->event,
|
||||
&ctx->monitor_source,
|
||||
udev_monitor_get_fd(ctx->monitor),
|
||||
EPOLLIN,
|
||||
ratbagd_monitor_event,
|
||||
ctx);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ratbagd_new(struct ratbagd **out)
|
||||
{
|
||||
_cleanup_(ratbagd_freep) struct ratbagd *ctx = NULL;
|
||||
int r;
|
||||
|
||||
ctx = calloc(1, sizeof(*ctx));
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
r = sd_event_default(&ctx->event);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_event_set_watchdog(ctx->event, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
ctx->lib_ctx = ratbag_create_context(&ratbagd_lib_interface, ctx);
|
||||
if (!ctx->lib_ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
if (verbose)
|
||||
ratbag_log_set_priority(ctx->lib_ctx,
|
||||
RATBAG_LOG_PRIORITY_DEBUG);
|
||||
|
||||
r = ratbagd_init_monitor(ctx);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_open_system(&ctx->bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_add_object_vtable(ctx->bus,
|
||||
NULL,
|
||||
"/org/freedesktop/ratbag1",
|
||||
"org.freedesktop.ratbag1.Manager",
|
||||
ratbagd_vtable,
|
||||
ctx);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_add_fallback_vtable(ctx->bus,
|
||||
NULL,
|
||||
"/org/freedesktop/ratbag1/device",
|
||||
"org.freedesktop.ratbag1.Device",
|
||||
ratbagd_device_vtable,
|
||||
ratbagd_find_device,
|
||||
ctx);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_add_node_enumerator(ctx->bus,
|
||||
NULL,
|
||||
"/org/freedesktop/ratbag1/device",
|
||||
ratbagd_list_devices,
|
||||
ctx);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_request_name(ctx->bus, "org.freedesktop.ratbag1", 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_attach_event(ctx->bus, ctx->event, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*out = ctx;
|
||||
ctx = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ratbagd_run_enumerate(struct ratbagd *ctx)
|
||||
{
|
||||
struct udev_list_entry *list, *iter;
|
||||
struct udev_enumerate *e;
|
||||
struct udev *udev;
|
||||
int r;
|
||||
|
||||
udev = udev_monitor_get_udev(ctx->monitor);
|
||||
|
||||
e = udev_enumerate_new(udev);
|
||||
if (!e)
|
||||
return -ENOMEM;
|
||||
|
||||
r = udev_enumerate_add_match_subsystem(e, "input");
|
||||
if (r < 0)
|
||||
goto exit;
|
||||
|
||||
r = udev_enumerate_add_match_is_initialized(e);
|
||||
if (r < 0)
|
||||
goto exit;
|
||||
|
||||
r = udev_enumerate_scan_devices(e);
|
||||
if (r < 0)
|
||||
goto exit;
|
||||
|
||||
list = udev_enumerate_get_list_entry(e);
|
||||
udev_list_entry_foreach(iter, list) {
|
||||
struct udev_device *udevice;
|
||||
const char *p;
|
||||
|
||||
p = udev_list_entry_get_name(iter);
|
||||
udevice = udev_device_new_from_syspath(udev, p);
|
||||
if (udevice)
|
||||
ratbagd_process_device(ctx, udevice);
|
||||
udev_device_unref(udevice);
|
||||
}
|
||||
|
||||
r = 0;
|
||||
|
||||
exit:
|
||||
udev_enumerate_unref(e);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int ratbagd_run(struct ratbagd *ctx)
|
||||
{
|
||||
int r;
|
||||
|
||||
/*
|
||||
* TODO: We should support exit-on-idle and bus-activation. Note that
|
||||
* we don't store any state on our own, hence, all we have to do
|
||||
* is to make sure we advertise device add/remove events via the
|
||||
* bus (in case there is a listener).
|
||||
* To track such events, we should store devices we already
|
||||
* advertised in /run/ratbagd/devices/ and read it out on
|
||||
* activation.
|
||||
* Note that this feature requires udev-activation, which might
|
||||
* not be possible, yet.
|
||||
*/
|
||||
|
||||
r = ratbagd_run_enumerate(ctx);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_event_loop(ctx->event);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct ratbagd *ctx = NULL;
|
||||
int r;
|
||||
|
||||
if (argc > 1) {
|
||||
if (streq(argv[1], "--verbose")) {
|
||||
verbose = true;
|
||||
} else {
|
||||
fprintf(stderr, "Usage: %s [--verbose]\n",
|
||||
program_invocation_short_name);
|
||||
r = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
r = ratbagd_new(&ctx);
|
||||
if (r < 0)
|
||||
goto exit;
|
||||
|
||||
r = ratbagd_run(ctx);
|
||||
|
||||
exit:
|
||||
ratbagd_free(ctx);
|
||||
|
||||
if (r < 0) {
|
||||
errno = -r;
|
||||
log_error("Failed: %m\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
170
ratbagd/src/ratbagd.h
Normal file
170
ratbagd/src/ratbagd.h
Normal file
@@ -0,0 +1,170 @@
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
This file is part of ratbagd.
|
||||
|
||||
Copyright 2015 David Herrmann <dh.herrmann@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the next
|
||||
paragraph) shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
***/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <libratbag.h>
|
||||
#include <libudev.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <systemd/sd-event.h>
|
||||
#include "shared-macro.h"
|
||||
#include "shared-rbtree.h"
|
||||
|
||||
struct ratbagd;
|
||||
struct ratbagd_device;
|
||||
struct ratbagd_profile;
|
||||
struct ratbagd_resolution;
|
||||
struct ratbagd_button;
|
||||
struct ratbagd_led;
|
||||
|
||||
void log_verbose(const char *fmt, ...) _printf_(1, 2);
|
||||
void log_error(const char *fmt, ...) _printf_(1, 2);
|
||||
|
||||
/*
|
||||
* Profiles
|
||||
*/
|
||||
|
||||
extern const sd_bus_vtable ratbagd_profile_vtable[];
|
||||
|
||||
int ratbagd_profile_new(struct ratbagd_profile **out,
|
||||
struct ratbagd_device *device,
|
||||
struct ratbag_profile *lib_profile,
|
||||
unsigned int index);
|
||||
struct ratbagd_profile *ratbagd_profile_free(struct ratbagd_profile *profile);
|
||||
const char *ratbagd_profile_get_path(struct ratbagd_profile *profile);
|
||||
bool ratbagd_profile_is_active(struct ratbagd_profile *profile);
|
||||
bool ratbagd_profile_is_default(struct ratbagd_profile *profile);
|
||||
unsigned int ratbagd_profile_get_index(struct ratbagd_profile *profile);
|
||||
int ratbagd_profile_register_resolutions(struct sd_bus *bus,
|
||||
struct ratbagd_device *device,
|
||||
struct ratbagd_profile *profile);
|
||||
int ratbagd_profile_register_buttons(struct sd_bus *bus,
|
||||
struct ratbagd_device *device,
|
||||
struct ratbagd_profile *profile);
|
||||
int ratbagd_profile_register_leds(struct sd_bus *bus,
|
||||
struct ratbagd_device *device,
|
||||
struct ratbagd_profile *profile);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_profile *, ratbagd_profile_free);
|
||||
|
||||
/*
|
||||
* Resolutions
|
||||
*/
|
||||
extern const sd_bus_vtable ratbagd_resolution_vtable[];
|
||||
|
||||
int ratbagd_resolution_new(struct ratbagd_resolution **out,
|
||||
struct ratbagd_device *device,
|
||||
struct ratbagd_profile *profile,
|
||||
struct ratbag_resolution *lib_resolution,
|
||||
unsigned int index);
|
||||
struct ratbagd_resolution *ratbagd_resolution_free(struct ratbagd_resolution *resolution);
|
||||
const char *ratbagd_resolution_get_path(struct ratbagd_resolution *resolution);
|
||||
bool ratbagd_resolution_is_active(struct ratbagd_resolution *resolution);
|
||||
bool ratbagd_resolution_is_default(struct ratbagd_resolution *resolution);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_resolution *, ratbagd_resolution_free);
|
||||
|
||||
/*
|
||||
* Buttons
|
||||
*/
|
||||
extern const sd_bus_vtable ratbagd_button_vtable[];
|
||||
|
||||
int ratbagd_button_new(struct ratbagd_button **out,
|
||||
struct ratbagd_device *device,
|
||||
struct ratbagd_profile *profile,
|
||||
struct ratbag_button *lib_button,
|
||||
unsigned int index);
|
||||
struct ratbagd_button *ratbagd_button_free(struct ratbagd_button *button);
|
||||
const char *ratbagd_button_get_path(struct ratbagd_button *button);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_button *, ratbagd_button_free);
|
||||
|
||||
/*
|
||||
* Leds
|
||||
*/
|
||||
extern const sd_bus_vtable ratbagd_led_vtable[];
|
||||
|
||||
int ratbagd_led_new(struct ratbagd_led **out,
|
||||
struct ratbagd_device *device,
|
||||
struct ratbagd_profile *profile,
|
||||
struct ratbag_led *lib_led,
|
||||
unsigned int index);
|
||||
struct ratbagd_led *ratbagd_led_free(struct ratbagd_led *led);
|
||||
const char *ratbagd_led_get_path(struct ratbagd_led *led);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_led *, ratbagd_led_free);
|
||||
|
||||
/*
|
||||
* Devices
|
||||
*/
|
||||
|
||||
extern const sd_bus_vtable ratbagd_device_vtable[];
|
||||
|
||||
int ratbagd_device_new(struct ratbagd_device **out,
|
||||
struct ratbagd *ctx,
|
||||
const char *name,
|
||||
struct ratbag_device *lib_device);
|
||||
struct ratbagd_device *ratbagd_device_free(struct ratbagd_device *device);
|
||||
const char *ratbagd_device_get_name(struct ratbagd_device *device);
|
||||
const char *ratbagd_device_get_path(struct ratbagd_device *device);
|
||||
unsigned int ratbagd_device_get_num_buttons(struct ratbagd_device *device);
|
||||
unsigned int ratbagd_device_get_num_leds(struct ratbagd_device *device);
|
||||
|
||||
bool ratbagd_device_linked(struct ratbagd_device *device);
|
||||
void ratbagd_device_link(struct ratbagd_device *device);
|
||||
void ratbagd_device_unlink(struct ratbagd_device *device);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_device *, ratbagd_device_free);
|
||||
|
||||
struct ratbagd_device *ratbagd_device_lookup(struct ratbagd *ctx,
|
||||
const char *name);
|
||||
struct ratbagd_device *ratbagd_device_first(struct ratbagd *ctx);
|
||||
struct ratbagd_device *ratbagd_device_next(struct ratbagd_device *device);
|
||||
|
||||
#define RATBAGD_DEVICE_FOREACH(_device, _ctx) \
|
||||
for ((_device) = ratbagd_device_first(_ctx); \
|
||||
(_device); \
|
||||
(_device) = ratbagd_device_next(_device))
|
||||
|
||||
/*
|
||||
* Context
|
||||
*/
|
||||
|
||||
struct ratbagd {
|
||||
sd_event *event;
|
||||
struct ratbag *lib_ctx;
|
||||
struct udev_monitor *monitor;
|
||||
sd_event_source *monitor_source;
|
||||
sd_bus *bus;
|
||||
|
||||
RBTree device_map;
|
||||
size_t n_devices;
|
||||
};
|
||||
260
ratbagd/src/shared-macro.h
Normal file
260
ratbagd/src/shared-macro.h
Normal file
@@ -0,0 +1,260 @@
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
This file is part of ratbagd.
|
||||
|
||||
Copyright 2015 David Herrmann <dh.herrmann@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the next
|
||||
paragraph) shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
***/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
* We require:
|
||||
* sizeof(void*) == sizeof(long)
|
||||
* sizeof(long) == 4 || sizeof(long) == 8
|
||||
* sizeof(int) == 4
|
||||
* The linux kernel requires the same from the toolchain, so this should work
|
||||
* just fine.
|
||||
*/
|
||||
#if __SIZEOF_POINTER__ != __SIZEOF_LONG__
|
||||
# error "sizeof(void*) != sizeof(long)"
|
||||
#elif __SIZEOF_LONG__ != 4 && __SIZEOF_LONG__ != 8
|
||||
# error "sizeof(long) != 4 && sizeof(long) != 8"
|
||||
#elif __SIZEOF_INT__ != 4
|
||||
# error "sizeof(int) != 4"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Shortcuts for gcc attributes. See GCC manual for details.
|
||||
*/
|
||||
#define _alignas_(_x) __attribute__((aligned(__alignof(_x))))
|
||||
#define _alloc_(...) __attribute__((alloc_size(__VA_ARGS__)))
|
||||
#define _cleanup_(_x) __attribute__((cleanup(_x)))
|
||||
#define _const_ __attribute__((const))
|
||||
#define _deprecated_ __attribute__((deprecated))
|
||||
#define _hidden_ __attribute__((visibility("hidden")))
|
||||
#define _likely_(_x) (__builtin_expect(!!(_x), 1))
|
||||
#define _malloc_ __attribute__((malloc))
|
||||
#define _packed_ __attribute__((packed))
|
||||
#define _printf_(_a, _b) __attribute__((format (printf, _a, _b)))
|
||||
#define _public_ __attribute__((visibility("default")))
|
||||
#define _pure_ __attribute__((pure))
|
||||
#define _sentinel_ __attribute__((sentinel))
|
||||
#define _unlikely_(_x) (__builtin_expect(!!(_x), 0))
|
||||
#define _unused_ __attribute__((unused))
|
||||
#define _weak_ __attribute__((weak))
|
||||
#define _weakref_(_x) __attribute__((weakref(#_x)))
|
||||
|
||||
/*
|
||||
* DECIMAL_TOKEN_MAX() - calculate maximum length of the decimal representation
|
||||
* of an integer
|
||||
* @_type: type of integer
|
||||
*/
|
||||
#define DECIMAL_TOKEN_MAX(_type) \
|
||||
(1 + (sizeof(_type) <= 1 ? 3 : \
|
||||
sizeof(_type) <= 2 ? 5 : \
|
||||
sizeof(_type) <= 4 ? 10 : \
|
||||
sizeof(_type) <= 8 ? 20 : \
|
||||
sizeof(int[-2 * (sizeof(_type) > 8)])))
|
||||
|
||||
/*
|
||||
* PROTECT_ERRNO: make sure a function protects errno
|
||||
*/
|
||||
static inline void reset_errno(int *saved_errno)
|
||||
{
|
||||
errno = *saved_errno;
|
||||
}
|
||||
#define PROTECT_ERRNO _cleanup_(reset_errno) _unused_ int saved_errno = errno
|
||||
|
||||
/*
|
||||
* DEFINE_TRIVIAL_CLEANUP_FUNC() - define helper suitable for _cleanup_()
|
||||
* @_type: type of object to cleanup
|
||||
* @_func: function to call on cleanup
|
||||
*/
|
||||
#define DEFINE_TRIVIAL_CLEANUP_FUNC(_type, _func) \
|
||||
static inline void _func##p(_type *_p) \
|
||||
{ \
|
||||
if (*_p) \
|
||||
_func(*_p); \
|
||||
} \
|
||||
struct __useless_struct_to_allow_trailing_semicolon__
|
||||
|
||||
/*
|
||||
* ELEMENTSOF() - number of array elements
|
||||
* @_array: C array
|
||||
*
|
||||
* This calculates the number of elements (compared to the byte size) of
|
||||
* the given C array. This returns (void) if the passed argument is not
|
||||
* actually a valid C array (decided at compile time).
|
||||
*/
|
||||
#define ELEMENTSOF(_array) \
|
||||
__extension__ (__builtin_choose_expr( \
|
||||
!__builtin_types_compatible_p(typeof(_array), \
|
||||
typeof(&*(_array))), \
|
||||
sizeof(_array) / sizeof((_array)[0]), \
|
||||
(void)0))
|
||||
|
||||
/*
|
||||
* negative_errno() - return negative errno
|
||||
*
|
||||
* This helper should be used to shut up gcc if you know 'errno' is
|
||||
* negative. Instead of "return -errno;", use "return negative_errno();"
|
||||
* It will suppress bogus gcc warnings in case it assumes 'errno' might
|
||||
* be 0 and thus the caller's error-handling might not be triggered.
|
||||
*/
|
||||
static inline int negative_errno(void)
|
||||
{
|
||||
return (errno <= 0) ? -EINVAL : -errno;
|
||||
}
|
||||
|
||||
/*
|
||||
* mfree() - free memory
|
||||
* @mem: memory to free
|
||||
*
|
||||
* This is basically the same as free(), but returns NULL. This makes free()
|
||||
* follow the same style as all our other destructors.
|
||||
*/
|
||||
static inline void *mfree(void *mem)
|
||||
{
|
||||
free(mem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* safe_close() - safe variant of close(2)
|
||||
* @fd: fd to close
|
||||
*
|
||||
* This is the same as close(2), but allows passing negative FDs, which makes
|
||||
* it a no-op. Furthermore, this always returns -1.
|
||||
*/
|
||||
static inline int safe_close(int fd)
|
||||
{
|
||||
if (fd >= 0) {
|
||||
PROTECT_ERRNO;
|
||||
close(fd);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Miscellaneous cleanup helpers
|
||||
* All these helpers are suitable for use with _cleanup_(). They call the helper
|
||||
* they're named after on the variable marked for cleanup.
|
||||
*/
|
||||
static inline void freep(void *p)
|
||||
{
|
||||
free(*(void**)p);
|
||||
}
|
||||
|
||||
static inline void safe_closep(int *fd)
|
||||
{
|
||||
safe_close(*fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* streq() - test whether two strings are equal
|
||||
* @_a: string A
|
||||
* @_b: string B
|
||||
*/
|
||||
#define streq(_a, _b) (!strcmp((_a), (_b)))
|
||||
|
||||
/*
|
||||
* streq_ptr() - test whether two strings are equal, considering NULL valid
|
||||
* @a: string A or NULL
|
||||
* @b: string B or NULL
|
||||
*/
|
||||
static inline bool streq_ptr(const char *a, const char *b)
|
||||
{
|
||||
return (a && b) ? streq(a, b) : (!a && !b);
|
||||
}
|
||||
|
||||
/*
|
||||
* startswith() - test prefix of a string
|
||||
* @s: string to test
|
||||
* @prefix: prefix to test for
|
||||
*
|
||||
* This returns a pointer to the first character in @s that follows @prefix. If
|
||||
* @s does not start with @prefix, NULL is returned.
|
||||
*/
|
||||
static inline char *startswith(const char *s, const char *prefix)
|
||||
{
|
||||
size_t l;
|
||||
|
||||
l = strlen(prefix);
|
||||
if (strncmp(s, prefix, l) == 0)
|
||||
return (char*) s + l;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* safe_atou() - safe variant of strtoul()
|
||||
* @s: string to parse
|
||||
* @ret_u: output storage for parsed integer
|
||||
*
|
||||
* This is a sane and safe variant of strtoul(), which rejects any invalid
|
||||
* input, or trailing garbage.
|
||||
*
|
||||
* Return: 0 on success, negative error code on failure.
|
||||
*/
|
||||
static inline int safe_atou(const char *s, unsigned int *ret_u)
|
||||
{
|
||||
char *x = NULL;
|
||||
unsigned long l;
|
||||
|
||||
assert(s);
|
||||
assert(ret_u);
|
||||
|
||||
errno = 0;
|
||||
l = strtoul(s, &x, 0);
|
||||
if (!x || x == s || *x || errno)
|
||||
return errno > 0 ? -errno : -EINVAL;
|
||||
|
||||
if ((unsigned long)(unsigned int)l != l)
|
||||
return -ERANGE;
|
||||
|
||||
*ret_u = (unsigned int)l;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* now() - returns current time in nano-seconds
|
||||
* @clock: clock to use
|
||||
*/
|
||||
static inline uint64_t now(clockid_t clock)
|
||||
{
|
||||
struct timespec spec = {};
|
||||
clock_gettime(clock, &spec);
|
||||
return spec.tv_sec * 1000ULL * 1000ULL * 1000ULL + spec.tv_nsec;
|
||||
}
|
||||
455
ratbagd/src/shared-rbtree.c
Normal file
455
ratbagd/src/shared-rbtree.c
Normal file
@@ -0,0 +1,455 @@
|
||||
/***
|
||||
This file is part of ratbagd.
|
||||
|
||||
Copyright 2015 David Herrmann <dh.herrmann@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the next
|
||||
paragraph) shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
***/
|
||||
|
||||
/*
|
||||
* Open Red-Black-Tree Implementation
|
||||
* You're highly recommended to read a paper on rb-trees before reading this
|
||||
* code. The reader is expected to be familiar with the different cases that
|
||||
* might occur during insertion and removal of elements. The comments in this
|
||||
* file do not contain a full prove for correctness.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "shared-rbtree.h"
|
||||
|
||||
RBNode *rbnode_leftmost(RBNode *n) {
|
||||
if (n)
|
||||
while (n->left)
|
||||
n = n->left;
|
||||
return n;
|
||||
}
|
||||
|
||||
RBNode *rbnode_rightmost(RBNode *n) {
|
||||
if (n)
|
||||
while (n->right)
|
||||
n = n->right;
|
||||
return n;
|
||||
}
|
||||
|
||||
RBNode *rbtree_first(RBTree *t) {
|
||||
return rbnode_leftmost(t->root);
|
||||
}
|
||||
|
||||
RBNode *rbtree_last(RBTree *t) {
|
||||
return rbnode_rightmost(t->root);
|
||||
}
|
||||
|
||||
RBNode *rbnode_next(RBNode *n) {
|
||||
RBNode *p;
|
||||
|
||||
if (!rbnode_linked(n))
|
||||
return NULL;
|
||||
|
||||
if (n->right)
|
||||
return rbnode_leftmost(n->right);
|
||||
|
||||
while ((p = rbnode_parent(n)) && n == p->right)
|
||||
n = p;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
RBNode *rbnode_prev(RBNode *n) {
|
||||
RBNode *p;
|
||||
|
||||
if (!rbnode_linked(n))
|
||||
return NULL;
|
||||
|
||||
if (n->left)
|
||||
return rbnode_rightmost(n->left);
|
||||
|
||||
while ((p = rbnode_parent(n)) && n == p->left)
|
||||
n = p;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static inline void rbnode_reparent(RBNode *n, RBNode *p, unsigned long c) {
|
||||
/* change color and/or parent of a node */
|
||||
rbtree_assert(!((unsigned long)p & 1));
|
||||
rbtree_assert(c < 2);
|
||||
n->__parent_and_color = (RBNode*)((unsigned long)p | c);
|
||||
}
|
||||
|
||||
static inline void rbtree_reparent(RBTree *t, RBNode *p, RBNode *old, RBNode *new) {
|
||||
/* change previous parent/root from old to new node */
|
||||
if (p) {
|
||||
if (p->left == old)
|
||||
p->left = new;
|
||||
else
|
||||
p->right = new;
|
||||
} else {
|
||||
t->root = new;
|
||||
}
|
||||
}
|
||||
|
||||
static inline RBNode *rbtree_paint_one(RBTree *t, RBNode *n) {
|
||||
RBNode *p, *g, *gg, *u, *x;
|
||||
|
||||
/*
|
||||
* Paint a single node according to RB-Tree rules. The node must
|
||||
* already be linked into the tree and painted red.
|
||||
* We repaint the node or rotate the tree, if required. In case a
|
||||
* recursive repaint is required, the next node to be re-painted
|
||||
* is returned.
|
||||
* p: parent
|
||||
* g: grandparent
|
||||
* gg: grandgrandparent
|
||||
* u: uncle
|
||||
* x: temporary
|
||||
*/
|
||||
|
||||
/* node is red, so we can access the parent directly */
|
||||
p = n->__parent_and_color;
|
||||
|
||||
if (!p) {
|
||||
/* Case 1:
|
||||
* We reached the root. Mark it black and be done. As all
|
||||
* leaf-paths share the root, the ratio of black nodes on each
|
||||
* path stays the same. */
|
||||
rbnode_reparent(n, p, RBNODE_BLACK);
|
||||
n = NULL;
|
||||
} else if (rbnode_black(p)) {
|
||||
/* Case 2:
|
||||
* The parent is already black. As our node is red, we did not
|
||||
* change the number of black nodes on any path, nor do we have
|
||||
* multiple consecutive red nodes. */
|
||||
n = NULL;
|
||||
} else if (p == p->__parent_and_color->left) { /* parent is red, so grandparent exists */
|
||||
g = p->__parent_and_color;
|
||||
gg = rbnode_parent(g);
|
||||
u = g->right;
|
||||
|
||||
if (u && rbnode_red(u)) {
|
||||
/* Case 3:
|
||||
* Parent and uncle are both red. We know the
|
||||
* grandparent must be black then. Repaint parent and
|
||||
* uncle black, the grandparent red and recurse into
|
||||
* the grandparent. */
|
||||
rbnode_reparent(p, g, RBNODE_BLACK);
|
||||
rbnode_reparent(u, g, RBNODE_BLACK);
|
||||
rbnode_reparent(g, gg, RBNODE_RED);
|
||||
n = g;
|
||||
} else {
|
||||
/* parent is red, uncle is black */
|
||||
|
||||
if (n == p->right) {
|
||||
/* Case 4:
|
||||
* We're the right child. Rotate on parent to
|
||||
* become left child, so we can handle it the
|
||||
* same as case 5. */
|
||||
x = n->left;
|
||||
p->right = n->left;
|
||||
n->left = p;
|
||||
if (x)
|
||||
rbnode_reparent(x, p, RBNODE_BLACK);
|
||||
rbnode_reparent(p, n, RBNODE_RED);
|
||||
p = n;
|
||||
}
|
||||
|
||||
/* 'n' is invalid from here on! */
|
||||
n = NULL;
|
||||
|
||||
/* Case 5:
|
||||
* We're the red left child or a red parent, black
|
||||
* grandparent and uncle. Rotate on grandparent and
|
||||
* switch color with parent. Number of black nodes on
|
||||
* each path stays the same, but we got rid of the
|
||||
* double red path. As the grandparent is still black,
|
||||
* we're done. */
|
||||
x = p->right;
|
||||
g->left = x;
|
||||
p->right = g;
|
||||
if (x)
|
||||
rbnode_reparent(x, g, RBNODE_BLACK);
|
||||
rbnode_reparent(p, gg, RBNODE_BLACK);
|
||||
rbnode_reparent(g, p, RBNODE_RED);
|
||||
rbtree_reparent(t, gg, g, p);
|
||||
}
|
||||
} else /* if (p == p->__parent_and_color->left) */ { /* same as above, but mirrored */
|
||||
g = p->__parent_and_color;
|
||||
gg = rbnode_parent(g);
|
||||
u = g->left;
|
||||
|
||||
if (u && rbnode_red(u)) {
|
||||
rbnode_reparent(p, g, RBNODE_BLACK);
|
||||
rbnode_reparent(u, g, RBNODE_BLACK);
|
||||
rbnode_reparent(g, gg, RBNODE_RED);
|
||||
n = g;
|
||||
} else {
|
||||
if (n == p->left) {
|
||||
x = n->right;
|
||||
p->left = n->right;
|
||||
n->right = p;
|
||||
if (x)
|
||||
rbnode_reparent(x, p, RBNODE_BLACK);
|
||||
rbnode_reparent(p, n, RBNODE_RED);
|
||||
p = n;
|
||||
}
|
||||
|
||||
n = NULL;
|
||||
|
||||
x = p->left;
|
||||
g->right = x;
|
||||
p->left = g;
|
||||
if (x)
|
||||
rbnode_reparent(x, g, RBNODE_BLACK);
|
||||
rbnode_reparent(p, gg, RBNODE_BLACK);
|
||||
rbnode_reparent(g, p, RBNODE_RED);
|
||||
rbtree_reparent(t, gg, g, p);
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static inline void rbtree_paint(RBTree *t, RBNode *n) {
|
||||
while (n)
|
||||
n = rbtree_paint_one(t, n);
|
||||
}
|
||||
|
||||
void rbtree_add(RBTree *t, RBNode *p, RBNode **l, RBNode *n) {
|
||||
n->__parent_and_color = p;
|
||||
n->left = n->right = NULL;
|
||||
*l = n;
|
||||
|
||||
rbtree_paint(t, n);
|
||||
}
|
||||
|
||||
static inline RBNode *rbtree_rebalance_one(RBTree *t, RBNode *p, RBNode *n) {
|
||||
RBNode *s, *x, *y, *g;
|
||||
|
||||
/*
|
||||
* Rebalance tree after a node was removed. This happens only if you
|
||||
* remove a black node and one path is now left with an unbalanced
|
||||
* number or black nodes.
|
||||
* This function assumes all paths through p and n have one black node
|
||||
* less than all other paths. If recursive fixup is required, the
|
||||
* current node is returned.
|
||||
*/
|
||||
|
||||
if (n == p->left) {
|
||||
s = p->right;
|
||||
if (rbnode_red(s)) {
|
||||
/* Case 3:
|
||||
* We have a red node as sibling. Rotate it onto our
|
||||
* side so we can later on turn it black. This way, we
|
||||
* gain the additional black node in our path. */
|
||||
g = rbnode_parent(p);
|
||||
x = s->left;
|
||||
p->right = x;
|
||||
s->left = p;
|
||||
rbnode_reparent(x, p, RBNODE_BLACK);
|
||||
rbnode_reparent(s, g, rbnode_color(p));
|
||||
rbnode_reparent(p, s, RBNODE_RED);
|
||||
rbtree_reparent(t, g, p, s);
|
||||
s = x;
|
||||
}
|
||||
|
||||
x = s->right;
|
||||
if (!x || rbnode_black(x)) {
|
||||
y = s->left;
|
||||
if (!y || rbnode_black(y)) {
|
||||
/* Case 4:
|
||||
* Our sibling is black and has only black
|
||||
* children. Flip it red and turn parent black.
|
||||
* This way we gained a black node in our path,
|
||||
* or we fix it recursively one layer up, which
|
||||
* will rotate the red sibling as parent. */
|
||||
rbnode_reparent(s, p, RBNODE_RED);
|
||||
if (rbnode_black(p))
|
||||
return p;
|
||||
|
||||
rbnode_reparent(p, rbnode_parent(p), RBNODE_BLACK);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Case 5:
|
||||
* Left child of our sibling is red, right one is black.
|
||||
* Rotate on parent so the right child of our sibling is
|
||||
* now red, and we can fall through to case 6. */
|
||||
x = y->right;
|
||||
s->left = y->right;
|
||||
y->right = s;
|
||||
p->right = y;
|
||||
if (x)
|
||||
rbnode_reparent(x, s, RBNODE_BLACK);
|
||||
x = s;
|
||||
s = y;
|
||||
}
|
||||
|
||||
/* Case 6:
|
||||
* The right child of our sibling is red. Rotate left and flip
|
||||
* colors, which gains us an additional black node in our path,
|
||||
* that was previously on our sibling. */
|
||||
g = rbnode_parent(p);
|
||||
y = s->left;
|
||||
p->right = y;
|
||||
s->left = p;
|
||||
rbnode_reparent(x, s, RBNODE_BLACK);
|
||||
if (y)
|
||||
rbnode_reparent(y, p, rbnode_color(y));
|
||||
rbnode_reparent(s, g, rbnode_color(p));
|
||||
rbnode_reparent(p, s, RBNODE_BLACK);
|
||||
rbtree_reparent(t, g, p, s);
|
||||
} else /* if (!n || n == p->right) */ { /* same as above, but mirrored */
|
||||
s = p->left;
|
||||
if (rbnode_red(s)) {
|
||||
g = rbnode_parent(p);
|
||||
x = s->right;
|
||||
p->left = x;
|
||||
s->right = p;
|
||||
rbnode_reparent(x, p, RBNODE_BLACK);
|
||||
rbnode_reparent(s, g, RBNODE_BLACK);
|
||||
rbnode_reparent(p, s, RBNODE_RED);
|
||||
rbtree_reparent(t, g, p, s);
|
||||
s = x;
|
||||
}
|
||||
|
||||
x = s->left;
|
||||
if (!x || rbnode_black(x)) {
|
||||
y = s->right;
|
||||
if (!y || rbnode_black(y)) {
|
||||
rbnode_reparent(s, p, RBNODE_RED);
|
||||
if (rbnode_black(p))
|
||||
return p;
|
||||
|
||||
rbnode_reparent(p, rbnode_parent(p), RBNODE_BLACK);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
x = y->left;
|
||||
s->right = y->left;
|
||||
y->left = s;
|
||||
p->left = y;
|
||||
if (x)
|
||||
rbnode_reparent(x, s, RBNODE_BLACK);
|
||||
x = s;
|
||||
s = y;
|
||||
}
|
||||
|
||||
g = rbnode_parent(p);
|
||||
y = s->right;
|
||||
p->left = y;
|
||||
s->right = p;
|
||||
rbnode_reparent(x, s, RBNODE_BLACK);
|
||||
if (y)
|
||||
rbnode_reparent(y, p, rbnode_color(y));
|
||||
rbnode_reparent(s, g, rbnode_color(p));
|
||||
rbnode_reparent(p, s, RBNODE_BLACK);
|
||||
rbtree_reparent(t, g, p, s);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void rbtree_rebalance(RBTree *t, RBNode *p) {
|
||||
RBNode *n = NULL;
|
||||
|
||||
while (p) {
|
||||
n = rbtree_rebalance_one(t, p, n);
|
||||
p = n ? rbnode_parent(n) : NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void rbtree_remove(RBTree *t, RBNode *n) {
|
||||
RBNode *p, *s, *gc, *x, *next = NULL;
|
||||
unsigned long c;
|
||||
|
||||
/*
|
||||
* To remove an interior node from a binary tree, we simply find its
|
||||
* successor, swap both nodes and then remove the node. Therefore, the
|
||||
* only interesting case is were the node to be removed has at most one
|
||||
* child.
|
||||
* p: parent
|
||||
* s: successor
|
||||
* gc: grand-...-child
|
||||
* x: temporary
|
||||
* next: next node to rebalance on
|
||||
*/
|
||||
|
||||
if (!n->left) {
|
||||
/* Case 1:
|
||||
* We have at most one child, which must be red. We're
|
||||
* guaranteed to be black then. Replace our node with the child
|
||||
* (in case it exists), but turn it black.
|
||||
* If no child exists, we need to rebalance, in case we were
|
||||
* black as our path now lost a black node. */
|
||||
p = rbnode_parent(n);
|
||||
c = rbnode_color(n);
|
||||
rbtree_reparent(t, p, n, n->right);
|
||||
if (n->right)
|
||||
rbnode_reparent(n->right, p, c);
|
||||
else
|
||||
next = (c == RBNODE_BLACK) ? p : NULL;
|
||||
} else if (!n->right) {
|
||||
/* case 1 mirrored (but n->left guaranteed non-NULL) */
|
||||
p = rbnode_parent(n);
|
||||
c = rbnode_color(n);
|
||||
rbnode_reparent(n->left, p, c);
|
||||
rbtree_reparent(t, p, n, n->left);
|
||||
} else {
|
||||
/* Case 2:
|
||||
* We're an interior node. Find our successor and swap it with
|
||||
* our node. Then remove our node. For performance reasons we
|
||||
* don't perform the full swap, but skip links that are about to
|
||||
* be removed, anyway. */
|
||||
s = n->right;
|
||||
if (!s->left) {
|
||||
/* right child is next, no need to touch grandchild */
|
||||
p = s;
|
||||
gc = s->right;
|
||||
} else {
|
||||
/* find successor and swap partially */
|
||||
s = rbnode_leftmost(s);
|
||||
p = rbnode_parent(s);
|
||||
|
||||
gc = s->right;
|
||||
p->left = s->right;
|
||||
s->right = n->right;
|
||||
rbnode_reparent(n->right, s, rbnode_color(n->right));
|
||||
}
|
||||
|
||||
/* node is partially swapped, now remove as in case 1 */
|
||||
|
||||
s->left = n->left;
|
||||
rbnode_reparent(n->left, s, rbnode_color(n->left));
|
||||
|
||||
x = rbnode_parent(n);
|
||||
c = rbnode_color(n);
|
||||
rbtree_reparent(t, x, n, s);
|
||||
if (gc) {
|
||||
rbnode_reparent(s, x, c);
|
||||
rbnode_reparent(gc, p, RBNODE_BLACK);
|
||||
} else {
|
||||
next = rbnode_black(s) ? p : NULL;
|
||||
rbnode_reparent(s, x, c);
|
||||
}
|
||||
}
|
||||
|
||||
if (next)
|
||||
rbtree_rebalance(t, next);
|
||||
}
|
||||
107
ratbagd/src/shared-rbtree.h
Normal file
107
ratbagd/src/shared-rbtree.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
This file is part of ratbagd.
|
||||
|
||||
Copyright 2015 David Herrmann <dh.herrmann@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the next
|
||||
paragraph) shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
***/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef RBTREE_DEBUG
|
||||
# include <assert.h>
|
||||
# define rbtree_assert(_x) assert(_x)
|
||||
#else
|
||||
# define rbtree_assert(_x)
|
||||
#endif
|
||||
|
||||
typedef struct RBTree RBTree;
|
||||
typedef struct RBNode RBNode;
|
||||
|
||||
enum {
|
||||
RBNODE_RED = 0,
|
||||
RBNODE_BLACK = 1,
|
||||
};
|
||||
|
||||
struct RBTree {
|
||||
RBNode *root;
|
||||
};
|
||||
|
||||
struct RBNode {
|
||||
RBNode *__parent_and_color;
|
||||
RBNode *left;
|
||||
RBNode *right;
|
||||
};
|
||||
|
||||
#define rbnode_of(_ptr, _type, _member) \
|
||||
__extension__ ({ \
|
||||
const typeof( ((_type*)0)->_member ) *__ptr = (_ptr); \
|
||||
__ptr ? (_type*)( (char*)__ptr - offsetof(_type, _member) ) : NULL; \
|
||||
})
|
||||
|
||||
#define RBNODE_INIT(var) ((RBNode){ .__parent_and_color = (&var) })
|
||||
|
||||
static inline RBNode *rbnode_init(RBNode *n) {
|
||||
*n = RBNODE_INIT(*n);
|
||||
return n;
|
||||
}
|
||||
|
||||
static inline RBNode *rbnode_parent(RBNode *n) {
|
||||
return (RBNode*)((unsigned long)n->__parent_and_color & ~1UL);
|
||||
}
|
||||
|
||||
static inline int rbnode_linked(RBNode *n) {
|
||||
return n && n->__parent_and_color != n;
|
||||
}
|
||||
|
||||
static inline unsigned long rbnode_color(RBNode *n) {
|
||||
return (unsigned long)n->__parent_and_color & 1UL;
|
||||
}
|
||||
|
||||
static inline int rbnode_red(RBNode *n) {
|
||||
return rbnode_color(n) == RBNODE_RED;
|
||||
}
|
||||
|
||||
static inline int rbnode_black(RBNode *n) {
|
||||
return rbnode_color(n) == RBNODE_BLACK;
|
||||
}
|
||||
|
||||
RBNode *rbnode_leftmost(RBNode *n);
|
||||
RBNode *rbnode_rightmost(RBNode *n);
|
||||
|
||||
RBNode *rbtree_first(RBTree *t);
|
||||
RBNode *rbtree_last(RBTree *t);
|
||||
RBNode *rbnode_next(RBNode *n);
|
||||
RBNode *rbnode_prev(RBNode *n);
|
||||
|
||||
void rbtree_add(RBTree *t, RBNode *p, RBNode **l, RBNode *n);
|
||||
void rbtree_remove(RBTree *t, RBNode *n);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
517
ratbagd/tools/ratbagctl.in
Executable file
517
ratbagd/tools/ratbagctl.in
Executable file
@@ -0,0 +1,517 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# vim: set expandtab shiftwidth=4 tabstop=4:
|
||||
#
|
||||
# This file is part of ratbagd.
|
||||
#
|
||||
# Copyright 2016 Red Hat, Inc.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||
# copy of this software and associated documentation files (the "Software"),
|
||||
# to deal in the Software without restriction, including without limitation
|
||||
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
# and/or sell copies of the Software, and to permit persons to whom the
|
||||
# Software is furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice (including the next
|
||||
# paragraph) shall be included in all copies or substantial portions of the
|
||||
# Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
# DEALINGS IN THE SOFTWARE.
|
||||
|
||||
from __future__ import print_function
|
||||
from gi.repository import GLib
|
||||
from gi.repository import Gio
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
class RatbagdDBusUnavailable(BaseException):
|
||||
"""
|
||||
Signals DBus is unavailable or the ratbagd daemon is not available.
|
||||
"""
|
||||
pass
|
||||
|
||||
class _RatbagdDBus(object):
|
||||
def __init__(self, interface, object_path):
|
||||
self._dbus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
|
||||
if self._dbus is None:
|
||||
raise RatbagdDBusUnavailable()
|
||||
|
||||
try:
|
||||
self._proxy = Gio.DBusProxy.new_sync(self._dbus,
|
||||
Gio.DBusProxyFlags.NONE,
|
||||
None,
|
||||
'org.freedesktop.ratbag1',
|
||||
object_path,
|
||||
'org.freedesktop.ratbag1.{}'.format(interface),
|
||||
None)
|
||||
except GLib.GError:
|
||||
raise RatbagdDBusUnavailable()
|
||||
|
||||
if self._proxy.get_name_owner() is None:
|
||||
raise RatbagdDBusUnavailable()
|
||||
|
||||
def dbus_property(self, property):
|
||||
p = self._proxy.get_cached_property(property)
|
||||
if p != None:
|
||||
return p.unpack()
|
||||
return p
|
||||
|
||||
def dbus_call(self, method, type, *value):
|
||||
val = GLib.Variant("({})".format(type), value )
|
||||
self._proxy.call_sync(method, val, Gio.DBusCallFlags.NO_AUTO_START, 500, None)
|
||||
|
||||
class Ratbagd(_RatbagdDBus):
|
||||
"""
|
||||
The ratbagd top-level object. Provides a list of devices available
|
||||
through ratbagd, actual interaction with the devices is via the
|
||||
RatbagdDevice, RatbagdProfile and RatbagdResolution objects.
|
||||
|
||||
Throws RatbagdDBusUnavailable when the DBus service is not available.
|
||||
"""
|
||||
def __init__(self):
|
||||
_RatbagdDBus.__init__(self, "Manager", '/org/freedesktop/ratbag1')
|
||||
self._devices = []
|
||||
result = self.dbus_property("Devices")
|
||||
if result != None:
|
||||
self._devices = [RatbagdDevice(objpath) for objpath in result]
|
||||
|
||||
@property
|
||||
def devices(self):
|
||||
"""
|
||||
A list of RatbagdDevice objects supported by ratbagd.
|
||||
"""
|
||||
return self._devices
|
||||
|
||||
class RatbagdDevice(_RatbagdDBus):
|
||||
CAP_SWITCHABLE_RESOLUTION = 1
|
||||
CAP_SWITCHABLE_PROFILE = 2
|
||||
CAP_BUTTON_KEY = 3
|
||||
CAP_LED = 4
|
||||
CAP_BUTTON_MACROS = 5
|
||||
CAP_DEFAULT_PROFILE = 6
|
||||
CAP_QUERY_CONFIGURATION = 7
|
||||
CAP_DISABLE_PROFILE = 8
|
||||
|
||||
"""
|
||||
Represents a ratbagd device.
|
||||
"""
|
||||
def __init__(self, object_path):
|
||||
_RatbagdDBus.__init__(self, "Device", object_path)
|
||||
self._devnode = self.dbus_property("Id")
|
||||
self._name = self.dbus_property("Name")
|
||||
self._svg = self.dbus_property("Svg")
|
||||
self._svg_path = self.dbus_property("SvgPath")
|
||||
|
||||
self._profiles = []
|
||||
self._active_profile = -1
|
||||
result = self.dbus_property("Profiles")
|
||||
if result != None:
|
||||
self._profiles = [RatbagdProfile(objpath) for objpath in result]
|
||||
self._active_profile = self.dbus_property("ActiveProfile")
|
||||
|
||||
self._caps = self.dbus_property("Capabilities")
|
||||
|
||||
@property
|
||||
def profiles(self):
|
||||
"""
|
||||
A list of RatbagdProfile objects provided by this device.
|
||||
"""
|
||||
return self._profiles
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""
|
||||
The device name, usually provided by the kernel.
|
||||
"""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def svg(self):
|
||||
"""
|
||||
The SVG file name. This function returns the file name only, not the
|
||||
absolute path to the file.
|
||||
"""
|
||||
return self._svg
|
||||
|
||||
@property
|
||||
def svg_path(self):
|
||||
"""
|
||||
The absolute SVG path. This function returns the full path to the
|
||||
svg file.
|
||||
"""
|
||||
return self._svg_path
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
"""
|
||||
A unique identifier for this device.
|
||||
"""
|
||||
return self._devnode
|
||||
|
||||
@property
|
||||
def active_profile(self):
|
||||
"""
|
||||
The currently active profile. This function returns a RatbagdProfile
|
||||
or None if no active profile was found.
|
||||
"""
|
||||
if self._active_profile == -1:
|
||||
return None
|
||||
return self._profiles[self._active_profile]
|
||||
|
||||
@property
|
||||
def capabilities(self):
|
||||
"""
|
||||
Return the capabilities of this device as an array.
|
||||
Capabilities not present on the device are not in the list. Thus use
|
||||
e.g.
|
||||
if RatbagdDevice.CAP_SWITCHABLE_RESOLUTION is in device.caps:
|
||||
do something
|
||||
"""
|
||||
return self._caps
|
||||
|
||||
def __eq__(self, other):
|
||||
return other and self._objpath == other._objpath
|
||||
|
||||
class RatbagdProfile(_RatbagdDBus):
|
||||
"""
|
||||
Represents a ratbagd profile
|
||||
"""
|
||||
def __init__(self, object_path):
|
||||
_RatbagdDBus.__init__(self, "Profile", object_path)
|
||||
self._objpath = object_path
|
||||
self._index = self.dbus_property("Index")
|
||||
|
||||
self._resolutions = []
|
||||
self._active_resolution_idx = -1
|
||||
self._default_resolution_idx = -1
|
||||
self._buttons = []
|
||||
|
||||
result = self.dbus_property("Resolutions")
|
||||
if result != None:
|
||||
self._resolutions = [RatbagdResolution(objpath) for objpath in result]
|
||||
self._active_resolution_idx = self.dbus_property("ActiveResolution")
|
||||
self._default_resolution_idx = self.dbus_property("DefaultResolution")
|
||||
|
||||
result = self.dbus_property("Buttons")
|
||||
if result != None:
|
||||
self._buttons = [RatbagdButton(objpath) for objpath in result]
|
||||
|
||||
@property
|
||||
def index(self):
|
||||
return self._index
|
||||
|
||||
@property
|
||||
def resolutions(self):
|
||||
"""
|
||||
A list of RatbagdResolution objects with this profile's resolutions.
|
||||
"""
|
||||
return self._resolutions
|
||||
|
||||
@property
|
||||
def active_resolution(self):
|
||||
"""
|
||||
The currently active resolution. This function returns a
|
||||
RatbagdResolution object or None.
|
||||
"""
|
||||
if self._active_resolution_idx == -1:
|
||||
return None
|
||||
return self._resolutions[self._active_resolution_idx]
|
||||
|
||||
@property
|
||||
def default_resolution(self):
|
||||
"""
|
||||
The default resolution. This function returns a RatbagdResolution
|
||||
object or None.
|
||||
"""
|
||||
if self._default_resolution_idx == -1:
|
||||
return None
|
||||
return self._resolutions[self._default_resolution_idx]
|
||||
|
||||
@property
|
||||
def buttons(self):
|
||||
"""
|
||||
A list of RatbagdButton objects with this profile's button
|
||||
mappings. Note that the list of buttons differs between profiles but
|
||||
the number of buttons is identical across profiles.
|
||||
"""
|
||||
return self._buttons
|
||||
|
||||
def __eq__(self, other):
|
||||
return self._objpath == other._objpath
|
||||
|
||||
class RatbagdResolution(_RatbagdDBus):
|
||||
CAP_INDIVIDUAL_REPORT_RATE = 1
|
||||
CAP_SEPARATE_XY_RESOLUTION = 2
|
||||
"""
|
||||
Represents a ratbagd resolution.
|
||||
"""
|
||||
def __init__(self, object_path):
|
||||
_RatbagdDBus.__init__(self, "Resolution", object_path)
|
||||
self._index = self.dbus_property("Index")
|
||||
self._xres = self.dbus_property("XResolution")
|
||||
self._yres = self.dbus_property("YResolution")
|
||||
self._rate = self.dbus_property("ReportRate")
|
||||
self._objpath = object_path
|
||||
|
||||
self._caps = self.dbus_property("Capabilities")
|
||||
|
||||
@property
|
||||
def resolution(self):
|
||||
"""Returns the tuple (xres, yres) with each resolution in DPI"""
|
||||
return (self._xres, self._yres)
|
||||
|
||||
@resolution.setter
|
||||
def resolution(self, res):
|
||||
return self.dbus_call("SetResolution", "uu", *res)
|
||||
|
||||
@property
|
||||
def report_rate(self):
|
||||
"""
|
||||
Returns the report rate in Hz.
|
||||
"""
|
||||
return self._rate
|
||||
|
||||
@report_rate.setter
|
||||
def report_rate(self, rate):
|
||||
return self.dbus_call("SetReportRate", "u", rate)
|
||||
|
||||
@property
|
||||
def capabilities(self):
|
||||
"""
|
||||
Return the capabilities of this device as a list.
|
||||
Capabilities not present on the device are not in the list. Thus use
|
||||
e.g.
|
||||
if RatbagdResolution.CAP_SEPARATE_XY_RESOLUTION is in resolution.caps:
|
||||
do something
|
||||
"""
|
||||
return self._caps
|
||||
|
||||
def __eq__(self, other):
|
||||
return self._objpath == other._objpath
|
||||
|
||||
class RatbagdButton(_RatbagdDBus):
|
||||
"""
|
||||
Represents a ratbagd button.
|
||||
"""
|
||||
def __init__(self, object_path):
|
||||
_RatbagdDBus.__init__(self, "Button", object_path)
|
||||
self._index = self.dbus_property("Index")
|
||||
self._button = self.dbus_property("ButtonMapping")
|
||||
|
||||
@property
|
||||
def index(self):
|
||||
return self._index
|
||||
|
||||
@property
|
||||
def button_type(self):
|
||||
return self.dbus_property("Type")
|
||||
|
||||
@property
|
||||
def action_type(self):
|
||||
return self.dbus_property("ActionType")
|
||||
|
||||
@property
|
||||
def special(self):
|
||||
self._special = self.dbus_property("SpecialMapping")
|
||||
return self._special
|
||||
|
||||
@special.setter
|
||||
def special(self, special):
|
||||
return self.dbus_call("SetSpecialMapping", "s", special)
|
||||
|
||||
@property
|
||||
def key(self):
|
||||
self._key = self.dbus_property("KeyMapping")
|
||||
return self._key
|
||||
|
||||
@key.setter
|
||||
def key(self, key, modifiers):
|
||||
return self.dbus_call("SetKeyMapping", "au", [key].append(modifiers))
|
||||
|
||||
@property
|
||||
def button(self):
|
||||
self._button = self.dbus_property("ButtonMapping")
|
||||
return self._button
|
||||
|
||||
@button.setter
|
||||
def button(self, button):
|
||||
return self.dbus_call("SetButtonMapping", "u", button)
|
||||
|
||||
def disable(self):
|
||||
return self.dbus_call("Disable", "")
|
||||
|
||||
def list_devices(r, args):
|
||||
for d in r.devices:
|
||||
print("{:10s} {:32s}".format(d.id + ":", d.name))
|
||||
|
||||
def find_device(r, args):
|
||||
dev = None
|
||||
for d in r.devices:
|
||||
if d.id == args.device:
|
||||
dev = d
|
||||
break;
|
||||
if dev is None:
|
||||
print("Unable to find device {}".format(args.device))
|
||||
sys.exit(1)
|
||||
return dev
|
||||
|
||||
def find_profile(r, args):
|
||||
d = find_device(r, args)
|
||||
try:
|
||||
p = d.profiles[args.profile]
|
||||
except IndexError:
|
||||
print("Invalid profile index {}".format(args.profile))
|
||||
sys.exit(1)
|
||||
return p, d
|
||||
|
||||
def find_resolution(r, args):
|
||||
p, d = find_profile(r, args)
|
||||
try:
|
||||
r = p.resolutions[args.resolution]
|
||||
except IndexError:
|
||||
print("Invalid resolution index {}".format(args.resolution))
|
||||
sys.exit(1)
|
||||
return r, p, d
|
||||
|
||||
def find_button(r, args):
|
||||
p, d = find_profile(r, args)
|
||||
try:
|
||||
b = p.buttons[args.button]
|
||||
except IndexError:
|
||||
print("Invalid button index {}".format(args.button))
|
||||
sys.exit(1)
|
||||
return b, p, d
|
||||
|
||||
def show_device(r, args):
|
||||
d = find_device(r, args)
|
||||
|
||||
|
||||
caps = { RatbagdDevice.CAP_SWITCHABLE_RESOLUTION : "switchable-resolution",
|
||||
RatbagdDevice.CAP_SWITCHABLE_PROFILE : "switchable-profile",
|
||||
RatbagdDevice.CAP_BUTTON_KEY : "button-keys",
|
||||
RatbagdDevice.CAP_BUTTON_MACROS : "button-macros",
|
||||
RatbagdDevice.CAP_DEFAULT_PROFILE : "default-profile",
|
||||
RatbagdDevice.CAP_QUERY_CONFIGURATION : "query-configuration",
|
||||
RatbagdDevice.CAP_DISABLE_PROFILE : "disable-profile",
|
||||
RatbagdDevice.CAP_LED : "led" }
|
||||
capabilities = [caps[c] for c in d.capabilities]
|
||||
|
||||
print("{} - {}".format(d.id, d.name))
|
||||
print(" SVG: {}".format(d.svg))
|
||||
print(" Capabilities: {}".format(", ".join(capabilities)))
|
||||
print("Number of Profiles: {}".format(len(d.profiles)))
|
||||
active = -1
|
||||
for i, p in enumerate(d.profiles):
|
||||
if p == d.active_profile:
|
||||
active = i
|
||||
break
|
||||
print(" Active Profile: {}".format(active))
|
||||
|
||||
def show_profile(r, args):
|
||||
p, d = find_profile(r, args)
|
||||
|
||||
print("Profile {} on {} ({})".format(args.profile, d.id, d.name))
|
||||
print(" Number of Buttons: {}".format(len(p.buttons)))
|
||||
print("Number of Resolutions: {}".format(len(p.resolutions)))
|
||||
active, default = -1, -1
|
||||
for i, r in enumerate(p.resolutions):
|
||||
if p.active_resolution == r:
|
||||
active = i
|
||||
if p.default_resolution == r:
|
||||
default = i
|
||||
|
||||
print(" Active Resolution: {}".format(active))
|
||||
print(" Default Resolution: {}".format(default))
|
||||
|
||||
def show_resolution(r, args):
|
||||
r, p, d = find_resolution(r, args)
|
||||
|
||||
print("Resolution {} on Profile {} on {} ({})".format(args.resolution,
|
||||
args.profile,
|
||||
d.id,
|
||||
d.name))
|
||||
print(" Report Rate: {}Hz".format(r.report_rate))
|
||||
if RatbagdResolution.CAP_SEPARATE_XY_RESOLUTION in r.capabilities:
|
||||
print(" Resolution: {}x{}dpi".format(*r.resolution))
|
||||
else:
|
||||
print(" Resolution: {}dpi".format(r.resolution[0]))
|
||||
|
||||
|
||||
caps = { RatbagdResolution.CAP_INDIVIDUAL_REPORT_RATE : "individual-report-rate",
|
||||
RatbagdResolution.CAP_SEPARATE_XY_RESOLUTION : "separate-xy-resolution" }
|
||||
capabilities = [caps[c] for c in r.capabilities]
|
||||
print(" Capabilities: {}".format(", ".join(capabilities)))
|
||||
|
||||
def show_button(r, args):
|
||||
b, p, d = find_button(r, args)
|
||||
|
||||
print("Button {} on Profile {} on {} ({})".format(args.button,
|
||||
args.profile,
|
||||
d.id,
|
||||
d.name))
|
||||
|
||||
print(" Type: {}".format(b.button_type))
|
||||
print(" Action Type: {}".format(b.action_type))
|
||||
|
||||
if b.action_type == "button":
|
||||
print(" Button Mapping: {}".format(b.button))
|
||||
elif b.action_type == "key":
|
||||
print(" Key Mapping: {}".format(b.key))
|
||||
elif b.action_type == "special":
|
||||
print("Special Mapping: {}".format(b.special))
|
||||
|
||||
def make_parser():
|
||||
parser = argparse.ArgumentParser(description="Inspect and modify configurable mice")
|
||||
parser.add_argument("-V", "--version", action="version", version="@version@")
|
||||
subs = parser.add_subparsers(title="COMMANDS", help=None)
|
||||
|
||||
list = subs.add_parser("list-devices", help="List available configurable mice")
|
||||
list.set_defaults(func=list_devices)
|
||||
|
||||
show = subs.add_parser("show-device", help="Show device information")
|
||||
show.add_argument("device", metavar="event0", help="The device node")
|
||||
show.set_defaults(func=show_device)
|
||||
|
||||
profile = subs.add_parser("show-profile", help="Show profile information")
|
||||
profile.add_argument("device", metavar="event0", help="The device node")
|
||||
profile.add_argument("profile", metavar="0", help="The profile index", type=int)
|
||||
profile.set_defaults(func=show_profile)
|
||||
|
||||
resolution = subs.add_parser("show-resolution", help="Show resolution information")
|
||||
resolution.add_argument("device", metavar="event0", help="The device node")
|
||||
resolution.add_argument("profile", metavar="0", help="The profile index", type=int)
|
||||
resolution.add_argument("resolution", metavar="0", help="The resolution index", type=int)
|
||||
resolution.set_defaults(func=show_resolution)
|
||||
|
||||
button = subs.add_parser("show-button", help="Show button information")
|
||||
button.add_argument("device", metavar="event0", help="The device node")
|
||||
button.add_argument("profile", metavar="0", help="The profile index", type=int)
|
||||
button.add_argument("button", metavar="0", help="The button index", type=int)
|
||||
button.set_defaults(func=show_button)
|
||||
|
||||
return parser
|
||||
|
||||
def main(argv):
|
||||
if not argv:
|
||||
argv = ["list-devices"]
|
||||
cmd = make_parser().parse_args(argv)
|
||||
|
||||
try:
|
||||
r = Ratbagd()
|
||||
if not r.devices:
|
||||
print("No devices available.")
|
||||
except RatbagdDBusUnavailable:
|
||||
print("Unable to connect to ratbagd over dbus")
|
||||
|
||||
cmd.func(r, cmd)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1:])
|
||||
Reference in New Issue
Block a user