2019-11-08 17:10:30 -07:00
|
|
|
/*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
|
|
* MA 02110-1301, USA.
|
2021-01-22 06:09:02 -07:00
|
|
|
*
|
2019-11-08 17:10:30 -07:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <map>
|
|
|
|
#include <array>
|
|
|
|
#include <string>
|
|
|
|
#include <iostream>
|
2020-05-11 08:49:58 -06:00
|
|
|
#include <fstream>
|
2019-11-08 17:10:30 -07:00
|
|
|
#include <exception>
|
2020-02-07 20:34:17 -07:00
|
|
|
#include <regex>
|
2021-01-22 06:09:02 -07:00
|
|
|
#include <type_traits>
|
|
|
|
#include <variant>
|
2019-11-08 17:10:30 -07:00
|
|
|
#include <getopt.h>
|
|
|
|
|
2020-07-12 12:35:13 -06:00
|
|
|
#include "include/rd_mouse.h"
|
2020-04-08 07:39:30 -06:00
|
|
|
#include "include/load_config.h"
|
2021-01-23 08:03:25 -07:00
|
|
|
#include "include/help.h"
|
2019-11-08 17:10:30 -07:00
|
|
|
|
2020-05-02 08:50:55 -06:00
|
|
|
// this is the default version string
|
|
|
|
// the version string gets overwritten by the makefile
|
|
|
|
#ifndef VERSION_STRING
|
|
|
|
#define VERSION_STRING "no version defined during compilation"
|
|
|
|
#endif
|
2020-04-08 07:10:02 -06:00
|
|
|
|
2020-08-14 16:41:22 -06:00
|
|
|
|
2021-01-22 06:09:02 -07:00
|
|
|
// this allows the creation of overloaded lambda functions
|
|
|
|
template< typename T1, typename T2 > struct overload : T1, T2 {
|
|
|
|
overload(T1 a, T2 b) : T1(a), T2(b) {};
|
|
|
|
using T1::operator();
|
|
|
|
using T2::operator();
|
|
|
|
};
|
|
|
|
|
2020-08-14 16:41:22 -06:00
|
|
|
|
2020-04-08 07:10:02 -06:00
|
|
|
// this function checks its arguments and opens the mouse accordingly
|
|
|
|
// (with vid and pid or with bus and device)
|
2020-07-12 14:02:29 -06:00
|
|
|
template< typename T >int open_mouse_wrapper( T &m, const bool flag_bus, const bool flag_device,
|
2020-08-14 16:41:22 -06:00
|
|
|
const std::string &string_bus, const std::string &string_device );
|
2020-04-08 07:10:02 -06:00
|
|
|
|
2020-08-14 16:41:22 -06:00
|
|
|
|
|
|
|
|
|
|
|
// main function
|
2019-11-08 17:10:30 -07:00
|
|
|
int main( int argc, char **argv ){
|
|
|
|
|
2020-08-14 16:41:22 -06:00
|
|
|
try{
|
|
|
|
// if no arguments: print help
|
|
|
|
if( argc == 1 ){
|
2021-01-23 08:03:25 -07:00
|
|
|
std::cout << mouse_m908_help;
|
2020-08-14 16:41:22 -06:00
|
|
|
return 0;
|
|
|
|
}
|
2019-11-08 17:10:30 -07:00
|
|
|
|
2020-08-14 16:41:22 -06:00
|
|
|
//command line options
|
|
|
|
static struct option long_options[] = {
|
|
|
|
{"help", no_argument, 0, 'h'},
|
|
|
|
{"config", required_argument, 0, 'c'},
|
|
|
|
{"profile", required_argument, 0, 'p'},
|
|
|
|
{"macro", required_argument, 0, 'm'},
|
|
|
|
{"number", required_argument, 0, 'n'},
|
|
|
|
{"bus", required_argument, 0, 'b'},
|
|
|
|
{"device", required_argument, 0, 'd'},
|
|
|
|
{"kernel-driver", no_argument, 0, 'k'},
|
|
|
|
{"version", no_argument, 0, 'v'},
|
|
|
|
{"dump", required_argument, 0, 'D'},
|
|
|
|
{"read", required_argument, 0, 'R'},
|
|
|
|
{"model", required_argument, 0, 'M'},
|
|
|
|
{0, 0, 0, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
bool flag_config = false, flag_profile = false;
|
|
|
|
bool flag_macro = false, flag_number = false;
|
|
|
|
bool flag_bus = false, flag_device = false;
|
|
|
|
bool flag_kernel_driver = false;
|
|
|
|
bool flag_dump_settings = false;
|
|
|
|
bool flag_read_settings = false;
|
|
|
|
|
|
|
|
std::string string_config, string_profile;
|
|
|
|
std::string string_macro, string_number;
|
|
|
|
std::string string_bus, string_device;
|
|
|
|
std::string string_dump, string_read;
|
2021-01-22 06:09:02 -07:00
|
|
|
std::string string_model = "";
|
2020-08-14 16:41:22 -06:00
|
|
|
|
|
|
|
//parse command line options
|
|
|
|
int c, option_index = 0;
|
|
|
|
while( (c = getopt_long( argc, argv, "hc:p:m:n:b:d:kvD:R:M:",
|
|
|
|
long_options, &option_index ) ) != -1 ){
|
|
|
|
|
|
|
|
switch( c ){
|
|
|
|
case 'h':
|
2021-01-23 08:03:25 -07:00
|
|
|
std::cout << mouse_m908_help;
|
2020-08-14 16:41:22 -06:00
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
flag_config = true;
|
|
|
|
string_config = optarg;
|
|
|
|
break;
|
|
|
|
case 'p':
|
|
|
|
flag_profile = true;
|
|
|
|
string_profile = optarg;
|
|
|
|
break;
|
|
|
|
case 'm':
|
|
|
|
flag_macro = true;
|
|
|
|
string_macro = optarg;
|
|
|
|
break;
|
|
|
|
case 'n':
|
|
|
|
flag_number = true;
|
|
|
|
string_number = optarg;
|
|
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
flag_bus = true;
|
|
|
|
string_bus = optarg;
|
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
flag_device = true;
|
|
|
|
string_device = optarg;
|
|
|
|
break;
|
|
|
|
case 'k':
|
|
|
|
flag_kernel_driver = true;
|
|
|
|
break;
|
|
|
|
case 'v':
|
2021-02-07 14:44:59 -07:00
|
|
|
std::cout << "Version: " << VERSION_STRING << "\n";
|
|
|
|
return 0;
|
2020-08-14 16:41:22 -06:00
|
|
|
break;
|
|
|
|
case 'D':
|
|
|
|
flag_dump_settings = true;
|
|
|
|
string_dump = optarg;
|
|
|
|
break;
|
|
|
|
case 'R':
|
|
|
|
flag_read_settings = true;
|
|
|
|
string_read = optarg;
|
|
|
|
break;
|
|
|
|
case 'M':
|
|
|
|
string_model = optarg;
|
|
|
|
break;
|
|
|
|
case '?':
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2019-11-08 17:10:30 -07:00
|
|
|
}
|
2021-02-07 14:44:59 -07:00
|
|
|
|
|
|
|
// print a list of valid model names
|
|
|
|
if( string_model == "?" ){
|
|
|
|
variant_loop<rd_mouse::mouse_variant>( [](auto m){
|
|
|
|
if( m.get_name() != rd_mouse::monostate::get_name() )
|
|
|
|
std::cout << m.get_name() << "\n";
|
|
|
|
} );
|
|
|
|
return 0;
|
|
|
|
}
|
2020-08-14 11:33:57 -06:00
|
|
|
|
2021-01-22 06:09:02 -07:00
|
|
|
rd_mouse::mouse_variant mouse;
|
2020-11-10 08:06:17 -07:00
|
|
|
|
2021-01-22 06:09:02 -07:00
|
|
|
if( string_model == "" )
|
|
|
|
mouse = rd_mouse::detect();
|
|
|
|
else
|
|
|
|
mouse = rd_mouse::detect(string_model);
|
|
|
|
|
2021-02-07 14:21:29 -07:00
|
|
|
if( std::holds_alternative<rd_mouse::monostate>(mouse) ){
|
2020-11-10 08:06:17 -07:00
|
|
|
throw std::string(
|
|
|
|
"Couldn't detect mouse.\n"
|
|
|
|
"- Check hardware and permissions (maybe you need to be root?)\n"
|
|
|
|
"- Try with the --model option\n"
|
|
|
|
"If nothing works please report this as a bug."
|
|
|
|
);
|
2020-08-14 11:33:57 -06:00
|
|
|
}
|
2020-07-12 14:02:29 -06:00
|
|
|
|
2021-01-22 06:09:02 -07:00
|
|
|
// lambda function to perform all actions on the mouse
|
|
|
|
auto perform_actions = overload(
|
2021-02-07 14:21:29 -07:00
|
|
|
[](rd_mouse::monostate){},
|
2021-01-22 06:09:02 -07:00
|
|
|
[&](auto& m){
|
|
|
|
|
|
|
|
// set whether to detach kernel driver
|
|
|
|
m.set_detach_kernel_driver( !flag_kernel_driver );
|
2020-11-12 07:10:59 -07:00
|
|
|
|
2021-01-22 06:09:02 -07:00
|
|
|
// open mouse, throws std::string in case of an error, handling in main()
|
|
|
|
open_mouse_wrapper( m, flag_bus, flag_device, string_bus, string_device );
|
2020-12-20 12:08:39 -07:00
|
|
|
|
2021-01-22 06:09:02 -07:00
|
|
|
try{
|
|
|
|
// read settings and dump raw data
|
|
|
|
if( flag_dump_settings ){
|
|
|
|
|
|
|
|
// dump to file or cout
|
|
|
|
if( string_dump != "-" ){
|
|
|
|
std::ofstream out( string_dump );
|
|
|
|
|
|
|
|
if( out.is_open() ){
|
|
|
|
// dump settings
|
|
|
|
m.dump_settings( out );
|
|
|
|
|
|
|
|
out.close();
|
|
|
|
} else{
|
|
|
|
throw std::string( "Couldn't open "+string_dump );
|
|
|
|
}
|
|
|
|
} else{
|
|
|
|
m.dump_settings( std::cout );
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// read settings and print in .ini format
|
|
|
|
if( flag_read_settings ){
|
|
|
|
|
|
|
|
// dump to file or cout
|
|
|
|
if( string_read != "-" ){
|
|
|
|
std::ofstream out( string_read );
|
|
|
|
|
|
|
|
if( out.is_open() ){
|
|
|
|
out << "# Model: " << m.get_name() << "\n";
|
|
|
|
// read settings
|
|
|
|
m.read_and_print_settings( out );
|
|
|
|
|
|
|
|
out.close();
|
|
|
|
} else{
|
|
|
|
throw std::string( "Couldn't open "+string_read );
|
|
|
|
}
|
|
|
|
} else{
|
|
|
|
std::cout << "# Model: " << m.get_name() << "\n";
|
|
|
|
m.read_and_print_settings( std::cout );
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// load and write config
|
|
|
|
if( flag_config ){
|
|
|
|
|
|
|
|
simple_ini_parser pt;
|
|
|
|
if( pt.read_ini( string_config ) != 0 )
|
|
|
|
throw std::string( "Could not open configuration file." );
|
|
|
|
|
|
|
|
//parse config file
|
|
|
|
for( int i = 1; i < 6; i++ ){
|
|
|
|
|
|
|
|
rd_mouse::rd_profile profile = (rd_mouse::rd_profile)(i - 1);
|
2020-07-12 14:02:29 -06:00
|
|
|
|
2021-01-22 06:09:02 -07:00
|
|
|
for( auto& lightmode : m.lightmode_strings() ){
|
|
|
|
if( pt.get("profile"+std::to_string(i)+".lightmode", "") == lightmode.second )
|
|
|
|
m.set_lightmode( profile, lightmode.first );
|
|
|
|
}
|
2020-08-14 16:41:22 -06:00
|
|
|
|
2021-01-22 06:09:02 -07:00
|
|
|
if( std::regex_match( pt.get("profile"+std::to_string(i)+".color", ""), std::regex("[0-9a-fA-F]{6}") ) ){
|
|
|
|
m.set_color( profile,
|
|
|
|
{(uint8_t)stoi( pt.get("profile"+std::to_string(i)+".color", "").substr(0,2), 0, 16),
|
|
|
|
(uint8_t)stoi( pt.get("profile"+std::to_string(i)+".color", "").substr(2,2), 0, 16),
|
|
|
|
(uint8_t)stoi( pt.get("profile"+std::to_string(i)+".color", "").substr(4,2), 0, 16)} );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( pt.get("profile"+std::to_string(i)+".brightness", "").length() != 0 ){
|
|
|
|
m.set_brightness( profile, (uint8_t)stoi( pt.get("profile"+std::to_string(i)+".brightness", ""), 0, 16) );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( pt.get("profile"+std::to_string(i)+".speed", "").length() != 0 ){
|
|
|
|
m.set_speed( profile, (uint8_t)stoi( pt.get("profile"+std::to_string(i)+".speed", ""), 0, 16) );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( pt.get("profile"+std::to_string(i)+".scrollspeed", "").length() != 0 ){
|
|
|
|
m.set_scrollspeed( profile, (uint8_t)stoi( pt.get("profile"+std::to_string(i)+".scrollspeed", ""), 0, 16) );
|
|
|
|
}
|
|
|
|
|
|
|
|
// DPI
|
|
|
|
for( int j = 1; j < 6; j++ ){
|
|
|
|
|
|
|
|
// DPI level disabled
|
|
|
|
if( pt.get("profile"+std::to_string(i)+".dpi"+std::to_string(j)+"_enable", "") == "0" )
|
|
|
|
m.set_dpi_enable( profile, j-1, false );
|
|
|
|
|
|
|
|
// DPI value
|
|
|
|
if( pt.get("profile"+std::to_string(i)+".dpi"+std::to_string(j), "").length() != 0 ){ // non-empty dpi value
|
|
|
|
|
|
|
|
if( m.set_dpi( profile, j-1, pt.get("profile"+std::to_string(i)+".dpi"+std::to_string(j), "") ) != 0 ) // if invalid dpi value
|
|
|
|
std::cerr << "Warning: Unknown DPI value " << pt.get("profile"+std::to_string(i)+".dpi"+std::to_string(j), "") << "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for( auto& report_rate : m.report_rate_strings() ){
|
|
|
|
if( pt.get("profile"+std::to_string(i)+".report_rate", "") == report_rate.second )
|
|
|
|
m.set_report_rate( profile, report_rate.first );
|
|
|
|
}
|
2020-08-14 16:41:22 -06:00
|
|
|
|
2021-01-22 06:09:02 -07:00
|
|
|
// button mapping
|
|
|
|
for( auto key : m.button_names() ){
|
|
|
|
if( pt.get("profile"+std::to_string(i)+"."+key.second, "").length() != 0 ){ m.set_key_mapping( profile, key.first, pt.get("profile"+std::to_string(i)+"."+key.second, "") ); }
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// write settings
|
|
|
|
m.write_settings();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// change active profile
|
|
|
|
if( flag_profile ){
|
|
|
|
|
|
|
|
// set profile
|
|
|
|
if( !std::regex_match( string_profile, std::regex("[1-5]") ) )
|
|
|
|
throw std::string( "Wrong argument, expected 1-5." );
|
2021-01-21 09:22:54 -07:00
|
|
|
|
2021-01-22 06:09:02 -07:00
|
|
|
m.set_profile( (rd_mouse::rd_profile)(std::stoi(string_profile) - 1) );
|
2021-01-21 09:22:54 -07:00
|
|
|
|
2021-01-22 06:09:02 -07:00
|
|
|
// write profile
|
|
|
|
m.write_profile();
|
|
|
|
|
|
|
|
}
|
2020-09-21 15:09:12 -06:00
|
|
|
|
2021-01-22 06:09:02 -07:00
|
|
|
// send all macros
|
|
|
|
if( flag_macro && !flag_number ){
|
|
|
|
|
|
|
|
// load macros
|
|
|
|
int r = m.set_all_macros( string_macro );
|
|
|
|
|
|
|
|
if( r != 0 )
|
|
|
|
throw std::string( "Couldn't load macros." );
|
|
|
|
|
|
|
|
// write macros
|
|
|
|
for( int i = 1; i < 16; i++ )
|
|
|
|
m.write_macro(i);
|
|
|
|
|
|
|
|
}
|
2020-09-24 09:36:53 -06:00
|
|
|
|
2021-01-22 06:09:02 -07:00
|
|
|
// send individual macro
|
|
|
|
if( flag_macro && flag_number ){
|
|
|
|
|
|
|
|
|
|
|
|
// set macro and macro slot (number)
|
|
|
|
int number;
|
|
|
|
|
|
|
|
if( std::regex_match( string_number, std::regex("[0-9]+") ) ){
|
|
|
|
number = (int)stoi(string_number);
|
|
|
|
} else{
|
|
|
|
throw std::string( "Wrong argument, expected 1-15." );
|
|
|
|
}
|
2020-09-21 15:09:12 -06:00
|
|
|
|
2021-01-22 06:09:02 -07:00
|
|
|
if( number < 1 || number > 15 )
|
|
|
|
throw std::string( "Wrong argument, expected 1-15." );
|
|
|
|
|
|
|
|
if( m.set_macro( number, string_macro ) != 0 )
|
|
|
|
throw std::string( "Couldn't load macro" );
|
|
|
|
|
|
|
|
// write macro
|
|
|
|
m.write_macro(number);
|
|
|
|
|
|
|
|
} else if( !flag_macro && flag_number ){
|
|
|
|
throw std::string( "Misssing option, --macro and --number must be used together." );
|
2020-09-21 15:09:12 -06:00
|
|
|
}
|
2020-08-14 16:41:22 -06:00
|
|
|
|
2020-07-12 14:48:41 -06:00
|
|
|
|
2021-01-22 06:09:02 -07:00
|
|
|
// error handling
|
|
|
|
} catch( std::string const &message ){ // close mouse, rethrow
|
|
|
|
|
|
|
|
m.close_mouse();
|
|
|
|
throw message;
|
|
|
|
|
|
|
|
} catch( std::exception const &e ){ // close mouse, rethrow
|
|
|
|
|
|
|
|
m.close_mouse();
|
|
|
|
throw e;
|
|
|
|
|
|
|
|
}
|
2020-08-14 16:41:22 -06:00
|
|
|
|
2021-01-22 06:09:02 -07:00
|
|
|
// close mouse
|
|
|
|
m.close_mouse();
|
|
|
|
|
2020-08-14 16:41:22 -06:00
|
|
|
}
|
2021-01-22 06:09:02 -07:00
|
|
|
);
|
2021-01-21 09:22:54 -07:00
|
|
|
|
2021-01-22 06:09:02 -07:00
|
|
|
std::visit( [&](auto&& arg){ perform_actions(arg); }, mouse );
|
2021-01-21 09:22:54 -07:00
|
|
|
|
2021-01-22 06:09:02 -07:00
|
|
|
} catch( std::string const &message ){ // print error message and quit
|
2020-05-13 11:28:09 -06:00
|
|
|
|
2021-01-22 06:09:02 -07:00
|
|
|
std::cerr << message << "\n";
|
|
|
|
return 1;
|
2020-05-13 11:28:09 -06:00
|
|
|
|
2021-01-22 06:09:02 -07:00
|
|
|
} catch( std::exception const &e ){ // handle exceptions
|
2020-05-13 11:28:09 -06:00
|
|
|
|
2021-01-22 06:09:02 -07:00
|
|
|
std::cerr << "An exception occured:\n" << e.what() << "\n";
|
|
|
|
return 1;
|
2020-05-13 11:28:09 -06:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-08-14 16:41:22 -06:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
template< typename T >int open_mouse_wrapper( T &m, const bool flag_bus, const bool flag_device,
|
|
|
|
const std::string &string_bus, const std::string &string_device ){
|
|
|
|
|
|
|
|
int open_return = 0; // open_mouse() return value
|
|
|
|
|
|
|
|
if( flag_bus != flag_device ){ // improper arguments
|
2020-02-07 20:46:27 -07:00
|
|
|
|
2020-08-14 16:41:22 -06:00
|
|
|
throw std::string( "Missing argument, --bus and --device must be used together." );
|
|
|
|
return 1;
|
2019-12-07 17:52:41 -07:00
|
|
|
|
2020-08-14 16:41:22 -06:00
|
|
|
} else if( flag_bus && flag_device ){ // open with bus and device
|
2020-02-07 20:46:27 -07:00
|
|
|
|
2020-08-14 16:41:22 -06:00
|
|
|
if( !std::regex_match( string_bus, std::regex("[0-9]+") ) ||
|
|
|
|
!std::regex_match( string_device, std::regex("[0-9]+") ) ){
|
|
|
|
|
|
|
|
throw std::string( "Wrong argument, expected number." );
|
2020-02-07 20:46:27 -07:00
|
|
|
return 1;
|
|
|
|
}
|
2019-12-07 17:52:41 -07:00
|
|
|
|
2020-08-14 16:41:22 -06:00
|
|
|
open_return = m.open_mouse_bus_device( stoi(string_bus), stoi(string_device) );
|
2019-12-07 17:52:41 -07:00
|
|
|
|
2020-08-14 16:41:22 -06:00
|
|
|
} else{ // open with vid and pid
|
2019-12-07 17:52:41 -07:00
|
|
|
|
2020-08-14 16:41:22 -06:00
|
|
|
open_return = m.open_mouse();
|
2019-12-07 17:52:41 -07:00
|
|
|
|
2020-08-14 16:41:22 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Could not open → print message
|
|
|
|
if( open_return != 0 ){
|
2019-12-12 19:02:46 -07:00
|
|
|
|
2020-08-14 16:41:22 -06:00
|
|
|
throw std::string(
|
|
|
|
"Couldn't open mouse.\n"
|
|
|
|
"- Check hardware and permissions (maybe you need to be root?)\n"
|
|
|
|
"- Try with or without the --kernel-driver option\n"
|
|
|
|
"- Try with the --model option\n"
|
|
|
|
"- Try with the --bus and --device options\n"
|
|
|
|
"If nothing works please report this as a bug."
|
|
|
|
);
|
2019-12-07 17:52:41 -07:00
|
|
|
|
2020-08-14 16:41:22 -06:00
|
|
|
return 1;
|
2019-12-07 17:52:41 -07:00
|
|
|
}
|
|
|
|
|
2019-11-08 17:10:30 -07:00
|
|
|
return 0;
|
|
|
|
}
|