+clang-format

This commit is contained in:
Jussi Kuokkanen
2023-07-26 16:33:11 +03:00
parent 1e829c094f
commit 070e2d830b
59 changed files with 1876 additions and 2081 deletions

View File

@@ -5,6 +5,6 @@
namespace TuxClocker::Crypto {
std::string sha256(std::string s);
std::string md5(std::string s);
std::string md5(std::string s);
};
}; // namespace TuxClocker::Crypto

View File

@@ -11,18 +11,17 @@ namespace TC = TuxClocker;
namespace TuxClocker::DBus {
template <typename T>
struct Result {
template <typename T> struct Result {
bool error;
T value;
friend QDBusArgument &operator<<(QDBusArgument &arg, const Result<T> r) {
arg.beginStructure();
arg << r.error << r.value;
arg.endStructure();
return arg;
}
friend const QDBusArgument &operator>>(const QDBusArgument &arg, Result<T> &r) {
arg.beginStructure();
arg >> r.error >> r.value;
@@ -39,7 +38,7 @@ struct Range {
QDBusVariant min, max;
friend QDBusArgument &operator<<(QDBusArgument &arg, const Range r) {
arg.beginStructure();
arg << r.min<< r.max;
arg << r.min << r.max;
arg.endStructure();
return arg;
}
@@ -52,13 +51,14 @@ struct Range {
TC::Device::AssignableInfo toAssignableInfo() {
auto min_v = min.variant();
auto max_v = max.variant();
auto im = static_cast<QVariant::Type>(QMetaType::Int);
auto dm = static_cast<QVariant::Type>(QMetaType::Double);
if (min_v.type() == im && max_v.type() == im)
return TC::Device::Range<int>(min_v.value<int>(), max_v.value<int>());
if (min_v.type() == dm && max_v.type() == dm)
return TC::Device::Range<double>(min_v.value<double>(), max_v.value<double>());
return TC::Device::Range<double>(
min_v.value<double>(), max_v.value<double>());
// Should never reach here
return TC::Device::Range<int>(0, 0);
}
@@ -107,8 +107,7 @@ struct DeviceNode {
}
};
template <typename T>
struct FlatTreeNode {
template <typename T> struct FlatTreeNode {
T value;
QVector<int> childIndices;
friend QDBusArgument &operator<<(QDBusArgument &arg, const FlatTreeNode<T> f) {
@@ -125,4 +124,4 @@ struct FlatTreeNode {
}
};
};
}; // namespace TuxClocker::DBus

View File

@@ -8,7 +8,7 @@
namespace TuxClocker {
namespace Device {
enum class AssignmentError {
InvalidArgument,
InvalidType,
@@ -21,15 +21,17 @@ enum class ReadError {
UnknownError
};
template <typename T>
struct Range {
Range() {};
Range(const T &min_, const T &max_) {min = min_, max = max_;}
template <typename T> struct Range {
Range(){};
Range(const T &min_, const T &max_) { min = min_, max = max_; }
T min, max;
};
struct Enumeration {
Enumeration(const std::string &name_, const uint &key_) {name = name_; key = key_;}
Enumeration(const std::string &name_, const uint &key_) {
name = name_;
key = key_;
}
std::string name;
uint key;
};
@@ -43,10 +45,11 @@ using AssignableInfo = std::variant<RangeInfo, std::vector<Enumeration>>;
class Assignable {
public:
Assignable(const std::function<std::optional<AssignmentError>(AssignmentArgument)> assignmentFunc,
AssignableInfo info,
const std::function<std::optional<AssignmentArgument>()> currentValueFunc,
std::optional<std::string> unit = std::nullopt) {
Assignable(
const std::function<std::optional<AssignmentError>(AssignmentArgument)> assignmentFunc,
AssignableInfo info,
const std::function<std::optional<AssignmentArgument>()> currentValueFunc,
std::optional<std::string> unit = std::nullopt) {
m_assignmentFunc = assignmentFunc;
m_assignableInfo = info;
m_currentValueFunc = currentValueFunc;
@@ -56,9 +59,9 @@ public:
return m_assignmentFunc(arg);
}
// What the Assignable is currently set to
std::optional<AssignmentArgument> currentValue() {return m_currentValueFunc();}
AssignableInfo assignableInfo() {return m_assignableInfo;}
std::optional<std::string> unit() {return m_unit;}
std::optional<AssignmentArgument> currentValue() { return m_currentValueFunc(); }
AssignableInfo assignableInfo() { return m_assignableInfo; }
std::optional<std::string> unit() { return m_unit; }
private:
AssignableInfo m_assignableInfo;
std::function<std::optional<AssignmentError>(AssignmentArgument)> m_assignmentFunc;
@@ -69,16 +72,13 @@ private:
class DynamicReadable {
public:
DynamicReadable() {}
DynamicReadable(const std::function<std::variant<ReadError,
ReadableValue>()> readFunc,
std::optional<std::string> unit = std::nullopt) {
DynamicReadable(const std::function<std::variant<ReadError, ReadableValue>()> readFunc,
std::optional<std::string> unit = std::nullopt) {
m_readFunc = readFunc;
m_unit = unit;
}
/*std::variant<ReadError, ReadableValue>*/ ReadResult read() {
return m_readFunc();
}
auto unit() {return m_unit;}
/*std::variant<ReadError, ReadableValue>*/ ReadResult read() { return m_readFunc(); }
auto unit() { return m_unit; }
private:
std::function<std::variant<ReadError, ReadableValue>()> m_readFunc;
std::optional<std::string> m_unit;
@@ -90,8 +90,8 @@ public:
m_value = value;
m_unit = unit;
}
ReadableValue value() {return m_value;}
std::optional<std::string> unit() {return m_unit;}
ReadableValue value() { return m_value; }
std::optional<std::string> unit() { return m_unit; }
private:
ReadableValue m_value;
std::optional<std::string> m_unit;
@@ -102,7 +102,6 @@ using ResetInfo = std::variant<std::function<void()>, AssignmentArgument>;
class Resettable {
private:
};
/* DeviceNode has a name, and optionally implements one of
@@ -115,5 +114,5 @@ struct DeviceNode {
std::string hash;
};
};
};
}; // namespace Device
}; // namespace TuxClocker

View File

@@ -5,28 +5,26 @@
namespace TuxClocker {
// Creates a function with no arguments from a function that takes some from inputs to the function and the function
template<typename T, typename... Args>
// Creates a function with no arguments from a function that takes some from inputs to the function
// and the function
template <typename T, typename... Args>
auto specializeFunction(Args... args, std::function<T(Args...)> func) {
return [args = std::make_tuple(std::move(args)...), &func]() {
return std::apply([&func](auto ...args) {
func(args...);
}, std::move(args));
};
return [args = std::make_tuple(std::move(args)...), &func]() {
return std::apply([&func](auto... args) { func(args...); }, std::move(args));
};
}
template<template<typename> typename List, typename In, typename Out>
template <template <typename> typename List, typename In, typename Out>
List<Out> map(List<In> list, std::function<Out(In)> func) {
List<Out> retval;
std::transform(list.begin(), list.end(), std::back_inserter(retval), func);
return retval;
}
template<typename List, typename T>
List filter(List list, std::function<bool(T)> func) {
template <typename List, typename T> List filter(List list, std::function<bool(T)> func) {
List retval;
std::copy_if(list.begin(), list.end(), std::back_inserter(retval), func);
return retval;
}
};
}; // namespace TuxClocker

View File

@@ -8,40 +8,41 @@
#include "Device.hpp"
#include "Tree.hpp"
#define TUXCLOCKER_PLUGIN_EXPORT(PluginType) \
extern "C" BOOST_SYMBOL_EXPORT PluginType __plugin; \
#define TUXCLOCKER_PLUGIN_EXPORT(PluginType) \
extern "C" BOOST_SYMBOL_EXPORT PluginType __plugin; \
PluginType __plugin;
#define TUXCLOCKER_PLUGIN_SYMBOL_NAME "__plugin"
namespace TuxClocker {
namespace Plugin {
namespace dll = boost::dll;
enum class InitializationError {
UnknownError
};
using namespace TuxClocker::Device;
class Plugin {
public:
static std::string pluginDirName() {return "plugins";}
static std::string pluginDirName() { return "plugins"; }
// Full path is efined at compile time
static std::string pluginPath();
};
class DevicePlugin {
public:
// Communicate plugin initialization success in this way since constructors cannot communicate it.
// Communicate plugin initialization success in this way since constructors cannot
// communicate it.
virtual std::optional<InitializationError> initializationError() = 0;
virtual TreeNode<DeviceNode> deviceRootNode() = 0;
virtual ~DevicePlugin() {}
// Helper for loading all DevicePlugin's
static std::optional<std::vector<boost::shared_ptr<DevicePlugin>>> loadPlugins();
};
};
};
}; // namespace Plugin
}; // namespace TuxClocker

View File

@@ -6,26 +6,23 @@
namespace TuxClocker {
template <typename T>
struct FlatTreeNode {
template <typename T> struct FlatTreeNode {
T value;
std::vector<int> childIndices;
};
template <typename T>
class TreeNode;
template <typename T>
struct FlatTree {
template <typename T> class TreeNode;
template <typename T> struct FlatTree {
std::vector<FlatTreeNode<T>> nodes;
static TreeNode<T> toTree(FlatTree<T> flatTree) {
std::function<void(TreeNode<T>*, uint)> recur;
std::function<void(TreeNode<T> *, uint)> recur;
recur = [&recur, flatTree](TreeNode<T> *node, uint i) {
auto c_indices = flatTree.nodes[i].childIndices;
for (auto index : c_indices)
node->appendChild(TreeNode<T>(flatTree.nodes[index].value));
uint j = 0;
for (auto &c_node : *(node->childrenPtr())) {
recur(&c_node, c_indices[j]);
@@ -42,23 +39,22 @@ struct FlatTree {
}
};
template <typename T>
class TreeNode {
template <typename T> class TreeNode {
public:
TreeNode() {};
TreeNode(T value) {m_value = value;}
void appendChild(T value) {m_children.push_back(TreeNode{value});}
void appendChild(TreeNode<T> node) {m_children.push_back(node);}
std::vector<TreeNode<T>> children() {return m_children;}
TreeNode(){};
TreeNode(T value) { m_value = value; }
void appendChild(T value) { m_children.push_back(TreeNode{value}); }
void appendChild(TreeNode<T> node) { m_children.push_back(node); }
std::vector<TreeNode<T>> children() { return m_children; }
// Needed for recursive tree construction
std::vector<TreeNode<T>> *childrenPtr() {return &m_children;}
std::vector<TreeNode<T>> *childrenPtr() { return &m_children; }
static void preorder(const TreeNode<T> node, std::function<void(const T)> func) {
func(node.m_value);
for (const auto child : node.m_children) {
preorder(child, func);
}
}
T value() {return m_value;}
T value() { return m_value; }
// Convert tree to array
FlatTree<T> toFlatTree() {
std::vector<FlatTreeNode<T>> nodes;
@@ -69,7 +65,7 @@ public:
for (auto &c_node : *(n->childrenPtr())) {
// Find the index of this child node
int j = 0, index = 0;
preorderByRef(node_ptr, [&j, &index, &c_node](TreeNode<T> *n) {
if (n == &c_node)
index = j;
@@ -84,12 +80,11 @@ public:
private:
T m_value;
std::vector<TreeNode<T>> m_children;
static void preorderByRef(TreeNode<T> *node,
std::function<void(TreeNode<T>*)> func) {
static void preorderByRef(TreeNode<T> *node, std::function<void(TreeNode<T> *)> func) {
func(node);
for (auto &c_node : *(node->childrenPtr()))
preorderByRef(&c_node, func);
}
};
};
}; // namespace TuxClocker

View File

@@ -12,63 +12,64 @@ extern "C" {
It is a tree structure provided by a module. */
enum tc_assignable_value_category {
TC_ASSIGNABLE_NONE,
TC_ASSIGNABLE_RANGE,
TC_ASSIGNABLE_ENUM
TC_ASSIGNABLE_NONE,
TC_ASSIGNABLE_RANGE,
TC_ASSIGNABLE_ENUM
};
// Is the range double or integer
enum tc_assignable_range_data_type {
TC_ASSIGNABLE_RANGE_INT,
TC_ASSIGNABLE_RANGE_DOUBLE
TC_ASSIGNABLE_RANGE_INT,
TC_ASSIGNABLE_RANGE_DOUBLE
};
typedef struct {
double min, max;
double min, max;
} tc_assignable_range_double_t;
typedef struct {
int64_t min, max;
int64_t min, max;
} tc_assignable_range_int_t;
typedef struct {
enum tc_assignable_range_data_type range_data_type;
union {
tc_assignable_range_double_t double_range;
tc_assignable_range_int_t int_range;
};
enum tc_assignable_range_data_type range_data_type;
union {
tc_assignable_range_double_t double_range;
tc_assignable_range_int_t int_range;
};
} tc_assignable_range_t;
typedef struct {
uint16_t property_count;
char **properties;
uint16_t property_count;
char **properties;
} tc_assignable_enum_t;
typedef struct tc_assignable_node_t {
// Assignable name eg. fan speed
char *name;
// Unit for assignable
char *unit;
// Assignable name eg. fan speed
char *name;
// Unit for assignable
char *unit;
// Callback for assignment (use NULL for a placeholder node)
int8_t (*assign_callback)(tc_variant_t value, const struct tc_assignable_node_t *node);
// Callback for assignment (use NULL for a placeholder node)
int8_t (*assign_callback)(tc_variant_t value, const struct tc_assignable_node_t *node);
// Possible values for tunables are either values from a range or enumerations
enum tc_assignable_value_category value_category;
union {
tc_assignable_enum_t enum_info;
tc_assignable_range_t range_info;
};
// Possible values for tunables are either values from a range or enumerations
enum tc_assignable_value_category value_category;
union {
tc_assignable_enum_t enum_info;
tc_assignable_range_t range_info;
};
struct tc_assignable_node_t *parent;
uint16_t children_count;
struct tc_assignable_node_t **children_nodes;
struct tc_assignable_node_t *parent;
uint16_t children_count;
struct tc_assignable_node_t **children_nodes;
} tc_assignable_node_t;
// Master data structure loaded by module loader
typedef struct {
tc_assignable_node_t *root_node;
const char *(*sha256_hash)(const tc_assignable_node_t*); // Callback to get a unique hash for a node
const char *(*sha256_hash)(
const tc_assignable_node_t *); // Callback to get a unique hash for a node
} tc_assignable_module_data_t;
/* Utility functions for assignables */
@@ -81,7 +82,8 @@ void tc_assignable_node_destroy(tc_assignable_node_t *node);
int8_t tc_assignable_node_add_child(tc_assignable_node_t *node, tc_assignable_node_t *child);
/* Utility functions for range and property info*/
void tc_assignable_node_set_data(tc_assignable_node_t *node, char *unit, char *name, int8_t (*assign_callback)(tc_variant_t, const tc_assignable_node_t*));
void tc_assignable_node_set_data(tc_assignable_node_t *node, char *unit, char *name,
int8_t (*assign_callback)(tc_variant_t, const tc_assignable_node_t *));
#ifdef __cplusplus
}

View File

@@ -5,43 +5,44 @@ extern "C" {
#endif
#include <stdint.h>
// Common definitions for tuxclocker
// Error values
#define TC_SUCCESS 0
#define TC_EGENERIC (-1)
#define TC_ENOMEM (-2)
#define TC_EINVAL (-3) // Invalid argument
#define TC_EINVAL (-3) // Invalid argument
#define TC_ENOPERM (-4) // Insufficient permissions
#define TC_EINVALPREREQ (-5) // Invalid prerequisite value (eg. trying to set fan speed with automatic mode active)
#define TC_EINVALPREREQ \
(-5) // Invalid prerequisite value (eg. trying to set fan speed with automatic mode active)
// Tagged union of data types for simulating function overloading
enum tc_data_types {
TC_TYPE_NONE,
TC_TYPE_INT,
TC_TYPE_UINT,
TC_TYPE_DOUBLE,
TC_TYPE_STRING,
TC_TYPE_STRING_ARR
TC_TYPE_NONE,
TC_TYPE_INT,
TC_TYPE_UINT,
TC_TYPE_DOUBLE,
TC_TYPE_STRING,
TC_TYPE_STRING_ARR
};
typedef struct {
enum tc_data_types data_type;
union {
int64_t int_value;
uint64_t uint_value;
double double_value;
char *string_value;
};
enum tc_data_types data_type;
union {
int64_t int_value;
uint64_t uint_value;
double double_value;
char *string_value;
};
} tc_variant_t;
typedef struct {
enum tc_data_types arg_type;
union {
int int_arg;
char **string_arr_arg;
};
enum tc_data_types arg_type;
union {
int int_arg;
char **string_arr_arg;
};
} tc_arg_t;
// Utility functions
@@ -52,15 +53,15 @@ void tc_str_arr_free(uint16_t str_count, char **strings);
// Binary search tree whose left node contains the smaller value
typedef struct tc_bin_node_ {
void *key;
void *value;
struct tc_bin_node_ *left;
struct tc_bin_node_ *right;
void *key;
void *value;
struct tc_bin_node_ *left;
struct tc_bin_node_ *right;
} tc_bin_node_t;
// Create a new node with key and data in the appropriate position
tc_bin_node_t *tc_bin_node_insert(tc_bin_node_t* node, void *key, void *value);
tc_bin_node_t *tc_bin_node_insert(tc_bin_node_t *node, void *key, void *value);
// Find the value associated with the key
void *tc_bin_node_find_value(tc_bin_node_t *node, const void *key);
// Destroy a node and its children

View File

@@ -9,7 +9,7 @@
#ifdef __cplusplus
extern "C" {
#endif
// Get filename list from a directory. Return value needs to be freed.
char **tc_fs_dir_filenames(const char *dir_name, uint16_t *file_count);

View File

@@ -1,3 +1 @@
#pragma once

View File

@@ -10,7 +10,7 @@
#ifdef __cplusplus
extern "C" {
#endif
// Bitmask values for module categories
#define TC_ASSIGNABLE (1)
#define TC_READABLE (1 << 1)
@@ -18,12 +18,12 @@ extern "C" {
#define TC_READABLE_DYNAMIC (1)
#define TC_READABLE_STATIC (1 << 1)
// Categories for modules.
enum tc_module_category {
TC_CATEGORY_ASSIGNABLE,
TC_CATEGORY_READABLE,
TC_CATEGORY_INTERFACE
TC_CATEGORY_ASSIGNABLE,
TC_CATEGORY_READABLE,
TC_CATEGORY_INTERFACE
};
// Maximum amount of modules loaded at once
@@ -31,7 +31,7 @@ enum tc_module_category {
// Default module path in case not defined
#ifndef TC_MODULE_PATH
#define TC_MODULE_PATH "/usr/lib/tuxclocker/modules"
#define TC_MODULE_PATH "/usr/lib/tuxclocker/modules"
#endif
#define TC_MODULE_DATABASE_NAME "tc_modules.json"
@@ -59,7 +59,8 @@ typedef struct {
int8_t (*init)();
int8_t (*close)();
uint64_t category;
// Since category specific data might be generated after calling module's 'init' callback, use function pointers to fetch it.
// Since category specific data might be generated after calling module's 'init' callback,
// use function pointers to fetch it.
union {
tc_readable_module_data_t (*readable_data)();
tc_assignable_module_data_t (*assignable_data)();
@@ -90,19 +91,23 @@ typedef struct tc_module_t {
// Callback for category specific main data structure of the module
void *(*category_data_callback)();
tc_module_category_info_t category_info;
} tc_module_t;
// Try to return the module handle matching the category and name. If it doesn't exist or there was a problem loading the module, returns NULL.
// Try to return the module handle matching the category and name. If it doesn't exist or there was
// a problem loading the module, returns NULL.
tc_module_t *tc_module_find(enum tc_module_category category, const char *name);
// Try to return all module handles matching 'category'. The return value needs to be freed in addition to tc_module_close()
// Try to return all module handles matching 'category'. The return value needs to be freed in
// addition to tc_module_close()
tc_module_t **tc_module_find_all_from_category(enum tc_module_category category, uint16_t *count);
// Convenience functions
tc_module_category_info_t tc_module_category_info_create(uint64_t mask, uint16_t num_categories, const tc_module_category_data_t *categories);
tc_module_category_info_t tc_module_category_info_create(
uint64_t mask, uint16_t num_categories, const tc_module_category_data_t *categories);
tc_module_category_data_t tc_module_category_data_create(uint64_t category, union module_data_callback_t u);
tc_module_category_data_t tc_module_category_data_create(
uint64_t category, union module_data_callback_t u);
// Close the module after successful find
void tc_module_close(tc_module_t *module);

View File

@@ -11,27 +11,26 @@ extern "C" {
#include <stdint.h>
#include <stdbool.h>
// Return value type for querying values
typedef struct {
bool valid;
tc_variant_t data;
bool valid;
tc_variant_t data;
} tc_readable_result_t;
typedef struct tc_readable_node_t_ {
char *name;
char *unit;
// Is the value of this node constant
bool constant;
// Instead of an update callback store the value for constants
union {
tc_readable_result_t (*value_callback)(const struct tc_readable_node_t_*);
tc_variant_t data;
};
uint16_t children_count;
struct tc_readable_node_t_ **children_nodes;
char *name;
char *unit;
// Is the value of this node constant
bool constant;
// Instead of an update callback store the value for constants
union {
tc_readable_result_t (*value_callback)(const struct tc_readable_node_t_ *);
tc_variant_t data;
};
uint16_t children_count;
struct tc_readable_node_t_ **children_nodes;
} tc_readable_node_t;
// Master data structure loaded by module loader
@@ -39,7 +38,8 @@ typedef struct {
uint64_t category_mask; // Which types of readable (dynamic or static) are implemented
tc_readable_node_t *root_static_node;
tc_readable_node_t *root_node;
const char *(*sha256_hash)(const tc_readable_node_t*); // Callback to get a unique hash for a node
const char *(*sha256_hash)(
const tc_readable_node_t *); // Callback to get a unique hash for a node
} tc_readable_module_data_t;
// Utility functions
@@ -58,7 +58,8 @@ tc_readable_node_t *tc_readable_node_add_new_child(tc_readable_node_t *parent);
// Set node data
void tc_readable_node_set_data(tc_readable_node_t *node, const char *name, const char *unit);
// Create a tc_readable_result from data, data type and validity. Avoids boilerplate in returning values from readable nodes.
// Create a tc_readable_result from data, data type and validity. Avoids boilerplate in returning
// values from readable nodes.
tc_readable_result_t tc_readable_result_create(enum tc_data_types type, void *data, bool valid);
#ifdef __cplusplus

View File

@@ -5,12 +5,12 @@
namespace TuxClocker::Crypto {
std::string sha256(std::string s) {
auto d = SHA256(reinterpret_cast<const unsigned char*>(s.c_str()), s.size(), 0);
auto d = SHA256(reinterpret_cast<const unsigned char *>(s.c_str()), s.size(), 0);
char out[(SHA256_DIGEST_LENGTH * 2) + 1];
for (int i = 0; i < SHA256_DIGEST_LENGTH; i++)
sprintf(out + (i * 2), "%02x", d[i]);
sprintf(out + (i * 2), "%02x", d[i]);
out[SHA256_DIGEST_LENGTH * 2] = '\0';
return std::string(out);
@@ -18,15 +18,15 @@ std::string sha256(std::string s) {
std::string md5(std::string s) {
unsigned char data[MD5_DIGEST_LENGTH];
MD5(reinterpret_cast<const unsigned char*>(s.c_str()), s.size(), data);
MD5(reinterpret_cast<const unsigned char *>(s.c_str()), s.size(), data);
char out[(MD5_DIGEST_LENGTH * 2) + 1];
for (int i = 0; i < MD5_DIGEST_LENGTH; i++)
sprintf(out + (i * 2), "%02x", data[i]);
sprintf(out + (i * 2), "%02x", data[i]);
out[MD5_DIGEST_LENGTH * 2] = '\0';
return std::string(out);
}
};
}; // namespace TuxClocker::Crypto

View File

@@ -7,23 +7,22 @@ using namespace TuxClocker::Plugin;
namespace fs = std::filesystem;
std::string Plugin::pluginPath() {
return TC_PLUGIN_PATH;
}
std::string Plugin::pluginPath() { return TC_PLUGIN_PATH; }
std::optional<std::vector<boost::shared_ptr<DevicePlugin>>> DevicePlugin::loadPlugins() {
std::vector<boost::shared_ptr<DevicePlugin>> retval;
//std::cout << pluginPath();
// std::cout << pluginPath();
for (const fs::directory_entry &entry : fs::directory_iterator(Plugin::pluginPath())) {
// Bleh, have to catch this unless I do more manual checks
try {
auto plugin = boost::dll::import_symbol<DevicePlugin>(entry.path().string(),
TUXCLOCKER_PLUGIN_SYMBOL_NAME);
auto plugin = boost::dll::import_symbol<DevicePlugin>(
entry.path().string(), TUXCLOCKER_PLUGIN_SYMBOL_NAME);
retval.push_back(plugin);
} catch (boost::system::system_error &e) {
}
catch (boost::system::system_error &e) {}
}
if (retval.empty()) return std::nullopt;
if (retval.empty())
return std::nullopt;
return retval;
}

View File

@@ -39,8 +39,8 @@ struct AMDGPUInfo {
class AMDPlugin : public DevicePlugin {
public:
AMDPlugin();
std::optional<InitializationError> initializationError() {return std::nullopt;}
void foo() {std::cout << "hi from AMD plugin\n";}
std::optional<InitializationError> initializationError() { return std::nullopt; }
void foo() { std::cout << "hi from AMD plugin\n"; }
TreeNode<DeviceNode> deviceRootNode();
~AMDPlugin();
private:
@@ -48,9 +48,7 @@ private:
std::vector<AMDGPUInfo> m_GPUInfoVec;
template <typename T>
static std::variant<ReadError, ReadableValue> libdrmRead(amdgpu_device_handle dev,
uint query,
std::optional<std::function<T(T)>> transformFunc = std::nullopt);
uint query, std::optional<std::function<T(T)>> transformFunc = std::nullopt);
};
AMDPlugin::~AMDPlugin() {
@@ -60,14 +58,15 @@ AMDPlugin::~AMDPlugin() {
}
template <typename T>
std::variant<ReadError, ReadableValue>AMDPlugin::libdrmRead(amdgpu_device_handle dev,
uint query,
std::optional<std::function<T(T)>> transformFunc) {
std::variant<ReadError, ReadableValue> AMDPlugin::libdrmRead(
amdgpu_device_handle dev, uint query, std::optional<std::function<T(T)>> transformFunc) {
T value;
auto retval = amdgpu_query_sensor_info(dev, query, sizeof(T), &value);
if (retval != 0) return ReadError::UnknownError;
if (!transformFunc.has_value()) return value;
if (retval != 0)
return ReadError::UnknownError;
if (!transformFunc.has_value())
return value;
return transformFunc.value()(value);
}
@@ -76,95 +75,89 @@ AMDPlugin::AMDPlugin() {
std::string path;
std::string filename;
};
std::vector<FSInfo> infoVec;
// Iterate through files in GPU device folder and find which ones have amdgpu loaded
for (const auto &entry : fs::directory_iterator(DRM_DIR_NAME)) {
// Check if entry is renderD file
if (entry.path().string().find(DRM_RENDER_MINOR_NAME) != std::string::npos) {
infoVec.push_back(FSInfo{entry.path().string(), entry.path().filename().string()});
infoVec.push_back(
FSInfo{entry.path().string(), entry.path().filename().string()});
}
}
for (const auto &info : infoVec) {
auto fd = open(info.path.c_str(), O_RDONLY);
auto v_ptr = drmGetVersion(fd);
amdgpu_device_handle dev;
uint32_t m, n;
int devInitRetval;
if (fd > 0 &&
v_ptr &&
if (fd > 0 && v_ptr &&
std::string(v_ptr->name).find(_AMDGPU_NAME) != std::string::npos &&
(devInitRetval = amdgpu_device_initialize(fd, &m, &n, &dev)) == 0) {
// Device uses amdgpu
// Find hwmon path if available
std::ostringstream stream;
stream << "/sys/class/drm/" << info.filename << "/device/hwmon";
std::optional<std::string> hwmonPath = std::nullopt;
try {
for (const auto &entry : fs::directory_iterator(stream.str())) {
if (entry.path().filename().string().find("hwmon") != std::string::npos) {
hwmonPath = entry.path().string();
break;
}
// Device uses amdgpu
// Find hwmon path if available
std::ostringstream stream;
stream << "/sys/class/drm/" << info.filename << "/device/hwmon";
std::optional<std::string> hwmonPath = std::nullopt;
try {
for (const auto &entry : fs::directory_iterator(stream.str())) {
if (entry.path().filename().string().find("hwmon") !=
std::string::npos) {
hwmonPath = entry.path().string();
break;
}
}
// This exception is only abnormal to get on Linux
catch (fs::filesystem_error &e) {}
m_GPUInfoVec.push_back(AMDGPUInfo{hwmonPath, dev});
continue;
}
// This exception is only abnormal to get on Linux
catch (fs::filesystem_error &e) {
}
m_GPUInfoVec.push_back(AMDGPUInfo{hwmonPath, dev});
continue;
}
//if (devInitRetval == 0) amdgpu_device_deinitialize(dev);
// if (devInitRetval == 0) amdgpu_device_deinitialize(dev);
close(fd);
drmFreeVersion(v_ptr);
}
// Add nodes with successful queries to device node
// Data structure for mapping to DynamicReadables
struct UnspecializedReadable {
std::function<std::variant<ReadError, ReadableValue>(AMDGPUInfo)> func;
std::optional<std::string> unit;
std::string nodeName;
};
// List of query functions for DynamicReadables
std::vector<UnspecializedReadable> rawNodes = {
{
[&](AMDGPUInfo info) {
return libdrmRead<uint>(info.devHandle,
AMDGPU_INFO_SENSOR_GPU_TEMP,
std::function<uint(uint)>([](uint val) {return val / 1000;}));
},
std::nullopt,
"Temperature"
}
};
{[&](AMDGPUInfo info) {
return libdrmRead<uint>(info.devHandle, AMDGPU_INFO_SENSOR_GPU_TEMP,
std::function<uint(uint)>([](uint val) { return val / 1000; }));
},
std::nullopt, "Temperature"}};
for (auto info : m_GPUInfoVec) {
auto availableReadables = filter(rawNodes,
std::function<bool(UnspecializedReadable)>([info](UnspecializedReadable ur) {
auto result = ur.func(info);
// Check if this node returns an error or not
match(result)(pattern(as<ReadError>(_)) = []{return false;},
pattern(_) = []{return true;});
return true;
}));
std::function<bool(UnspecializedReadable)>([info](UnspecializedReadable ur) {
auto result = ur.func(info);
// Check if this node returns an error or not
match(result)(
pattern(as<ReadError>(_)) = [] { return false; },
pattern(_) = [] { return true; });
return true;
}));
auto specializedNodes = map(availableReadables,
std::function<DynamicReadable(UnspecializedReadable)>(
[info] (UnspecializedReadable ur) {
//auto specializedFunc = specializeFunction<std::variant<ReadError,
//ReadableValue>, AMDGPUInfo>(info, ur.func);
return DynamicReadable([info, ur]() {
return ur.func(info);
});
std::function<DynamicReadable(UnspecializedReadable)>(
[info](UnspecializedReadable ur) {
// auto specializedFunc = specializeFunction<std::variant<ReadError,
// ReadableValue>, AMDGPUInfo>(info, ur.func);
return DynamicReadable([info, ur]() { return ur.func(info); });
}));
}
}
TreeNode<DeviceNode> AMDPlugin::deviceRootNode() {
return m_rootNode;
}
TreeNode<DeviceNode> AMDPlugin::deviceRootNode() { return m_rootNode; }
TUXCLOCKER_PLUGIN_EXPORT(AMDPlugin)

File diff suppressed because it is too large Load Diff

View File

@@ -33,31 +33,29 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
auto conn = QDBusConnection::systemBus();
QDBusInterface tuxclockerd("org.tuxclocker", "/", "org.tuxclocker", conn);
QDBusReply<QVector<TCDBus::FlatTreeNode<TCDBus::DeviceNode>>> reply =
tuxclockerd.call("flatDeviceTree");
QDBusReply<QVector<TCDBus::FlatTreeNode<TCDBus::DeviceNode>>> reply =
tuxclockerd.call("flatDeviceTree");
// Convert to non-dbus flat tree
FlatTree<TCDBus::DeviceNode> flatTree;
if (reply.isValid()) {
auto dbusFlatTree = reply.value();
for (auto &f_node : dbusFlatTree) {
//qDebug() << f_node.value.interface << f_node.value.path;
// qDebug() << f_node.value.interface << f_node.value.path;
FlatTreeNode<TCDBus::DeviceNode> node{
f_node.value,
f_node.childIndices.toStdVector()
};
f_node.value, f_node.childIndices.toStdVector()};
flatTree.nodes.push_back(node);
}
}
auto root = flatTree.toTree(flatTree);
/*auto view = new QTreeView;
view->setItemDelegate(new DeviceModelDelegate);
view->setModel(model);
setCentralWidget(view);*/
auto model = new DeviceModel(root);
auto browser = new DeviceBrowser(*model);
setCentralWidget(browser);

View File

@@ -2,5 +2,4 @@
class AbstractAssignableConnection {
public:
};

View File

@@ -10,17 +10,16 @@
// Forgive me for the sin of multiple inheritance
class AssignableItem : public QObject, public QStandardItem {
public:
AssignableItem(QObject *parent = nullptr) : QObject(parent), QStandardItem() {
}
bool committal() {return m_committed;}
AssignableItem(QObject *parent = nullptr) : QObject(parent), QStandardItem() {}
bool committal() { return m_committed; }
// Whether or not the set value shall be applied. Doesn't reset or change it.
void setCommittal(bool on) {m_committed = on;}
void setCommittal(bool on) { m_committed = on; }
void setData(const QVariant &v, int role = Qt::UserRole + 1);
signals:
void assignableDataChanged(QVariant value);
void committalChanged(bool on);
private:
Q_OBJECT
bool m_committed = false;
};

View File

@@ -7,18 +7,19 @@ namespace TC = TuxClocker;
class AssignableItemData {
public:
AssignableItemData() {m_enabled = false;}
AssignableItemData(TC::Device::AssignableInfo info, std::optional<QString> unit) : m_info(info) {
AssignableItemData() { m_enabled = false; }
AssignableItemData(TC::Device::AssignableInfo info, std::optional<QString> unit)
: m_info(info) {
m_unit = unit;
m_enabled = false;
}
TC::Device::AssignableInfo assignableInfo() {return m_info;}
bool committal() {return m_enabled;}
TC::Device::AssignableInfo assignableInfo() { return m_info; }
bool committal() { return m_enabled; }
// Whether or not the set value shall be applied. Doesn't reset or change it.
void setCommittal(bool on) {m_enabled = on;}
void setValue(QVariant v) {m_targetValue = v;}
QVariant value() {return m_targetValue;}
std::optional<QString> unit() {return m_unit;}
void setCommittal(bool on) { m_enabled = on; }
void setValue(QVariant v) { m_targetValue = v; }
QVariant value() { return m_targetValue; }
std::optional<QString> unit() { return m_unit; }
private:
bool m_enabled;
TC::Device::AssignableInfo m_info;

View File

@@ -1,6 +1,5 @@
#include "AssignableProxy.hpp"
#include <DBusTypes.hpp>
#include <QDBusReply>
#include <QDBusMessage>
@@ -13,22 +12,22 @@ Q_DECLARE_METATYPE(TCD::Result<int>)
Q_DECLARE_METATYPE(TCD::Result<QString>)
Q_DECLARE_METATYPE(TCD::Result<QDBusVariant>)
AssignableProxy::AssignableProxy(QString path, QDBusConnection conn,
QObject *parent) : QObject(parent) {
AssignableProxy::AssignableProxy(QString path, QDBusConnection conn, QObject *parent)
: QObject(parent) {
qDBusRegisterMetaType<TCD::Result<int>>();
qDBusRegisterMetaType<TCD::Result<QString>>();
qDBusRegisterMetaType<TCD::Result<QDBusVariant>>();
m_iface = new QDBusInterface("org.tuxclocker",
path, "org.tuxclocker.Assignable", conn, this);
m_iface =
new QDBusInterface("org.tuxclocker", path, "org.tuxclocker.Assignable", conn, this);
}
std::optional<AssignmentError> AssignableProxy::doApply(const QVariant &v) {
//qDebug() << v;
// qDebug() << v;
QDBusReply<TCD::Result<int>> reply = m_iface->call("assign", v);
if (reply.isValid()) {
TCD::Result<AssignmentError> ar{reply.value().error,
static_cast<AssignmentError>(reply.value().value)};
//qDebug("Success!");
TCD::Result<AssignmentError> ar{
reply.value().error, static_cast<AssignmentError>(reply.value().value)};
// qDebug("Success!");
return ar.toOptional();
}
// TODO: indicate dbus error
@@ -39,7 +38,7 @@ void AssignableProxy::apply() {
// A value hasn't been set yet
if (m_value.isNull())
return;
// Use QDBusVariant since otherwise tries to call with the wrong signature
QDBusVariant dv(m_value);
QVariant v;
@@ -51,13 +50,14 @@ void AssignableProxy::apply() {
void AssignableProxy::startConnection(std::shared_ptr<AssignableConnection> conn) {
m_assignableConnection = conn;
connect(conn.get(), &AssignableConnection::targetValueChanged, [this](auto targetValue, auto text) {
auto err = doApply(targetValue);
if (err.has_value())
emit connectionValueChanged(err.value(), text);
else
emit connectionValueChanged(targetValue, text);
});
connect(conn.get(), &AssignableConnection::targetValueChanged,
[this](auto targetValue, auto text) {
auto err = doApply(targetValue);
if (err.has_value())
emit connectionValueChanged(err.value(), text);
else
emit connectionValueChanged(targetValue, text);
});
// Emit started signal in case a connection emits a new value right away
emit connectionStarted();
m_assignableConnection->start();
@@ -70,18 +70,17 @@ std::optional<AssignmentArgument> toAssignmentArgument(TCD::Result<QDBusVariant>
auto type = static_cast<QMetaType::Type>(res.value.variant().type());
auto v = res.value.variant();
switch (type) {
case QMetaType::Int:
return v.value<int>();
case QMetaType::UInt:
return v.value<uint>();
case QMetaType::Double:
return v.value<double>();
default:
return std::nullopt;
}
switch (type) {
case QMetaType::Int:
return v.value<int>();
case QMetaType::UInt:
return v.value<uint>();
case QMetaType::Double:
return v.value<double>();
default:
return std::nullopt;
}
}
std::optional<AssignmentArgument> AssignableProxy::currentValue() {

View File

@@ -15,23 +15,22 @@ namespace TC = TuxClocker;
class AssignableProxy : public QObject {
public:
AssignableProxy(QString path, QDBusConnection conn,
QObject *parent = nullptr);
AssignableProxy(QString path, QDBusConnection conn, QObject *parent = nullptr);
void apply();
void startConnection(std::shared_ptr<AssignableConnection> conn);
// Stop connection and clear current connection
void stopConnection();
void setValue(QVariant v) {m_value = v;}
void setValue(QVariant v) { m_value = v; }
std::optional<TC::Device::AssignmentArgument> currentValue();
signals:
void applied(std::optional<TC::Device::AssignmentError>);
void connectionValueChanged(std::variant<QVariant, TC::Device::AssignmentError>,
QString text);
void connectionValueChanged(
std::variant<QVariant, TC::Device::AssignmentError>, QString text);
void connectionStarted();
void connectionStopped();
private:
Q_OBJECT
QVariant m_value;
QDBusInterface *m_iface;
// This is a bit of a peril but not sure if we can store interfaces any better...

View File

@@ -17,15 +17,15 @@ using namespace mpark::patterns;
using namespace TuxClocker::Device;
Q_DECLARE_METATYPE(AssignableItemData)
Q_DECLARE_METATYPE(AssignableProxy*)
Q_DECLARE_METATYPE(DynamicReadableProxy*)
Q_DECLARE_METATYPE(AssignableProxy *)
Q_DECLARE_METATYPE(DynamicReadableProxy *)
Q_DECLARE_METATYPE(TCDBus::Enumeration)
Q_DECLARE_METATYPE(TCDBus::Range)
Q_DECLARE_METATYPE(EnumerationVec)
Q_DECLARE_METATYPE(TCDBus::Result<QString>)
DeviceModel::DeviceModel(TC::TreeNode<TCDBus::DeviceNode> root, QObject *parent) :
QStandardItemModel(parent) {
DeviceModel::DeviceModel(TC::TreeNode<TCDBus::DeviceNode> root, QObject *parent)
: QStandardItemModel(parent) {
qDBusRegisterMetaType<TCDBus::Enumeration>();
qDBusRegisterMetaType<QVector<TCDBus::Enumeration>>();
qDBusRegisterMetaType<TCDBus::Range>();
@@ -33,111 +33,111 @@ DeviceModel::DeviceModel(TC::TreeNode<TCDBus::DeviceNode> root, QObject *parent)
/* Data storage:
- Interface column should store assignable info for editors
- Name colums should store the interface type for filtering
- Parametrization/connection data, where? */
- Name colums should store the interface type for filtering
- Parametrization/connection data, where? */
setColumnCount(2);
std::function<void(TC::TreeNode<TCDBus::DeviceNode> node,
QStandardItem*)> traverse;
std::function<void(TC::TreeNode<TCDBus::DeviceNode> node, QStandardItem *)> traverse;
traverse = [&traverse, this](auto node, auto item) {
auto conn = QDBusConnection::systemBus();
QDBusInterface nodeIface("org.tuxclocker",
node.value().path,"org.tuxclocker.Node", conn);
QDBusInterface nodeIface(
"org.tuxclocker", node.value().path, "org.tuxclocker.Node", conn);
auto nodeName = nodeIface.property("name").toString();
QList<QStandardItem*> rowItems;
QList<QStandardItem *> rowItems;
auto nameItem = new QStandardItem;
nameItem->setText(nodeName);
rowItems.append(nameItem);
p::match(node.value().interface) (
pattern("org.tuxclocker.Assignable") = [=, &rowItems]{
if_let(pattern(some(arg)) = setupAssignable(node, conn))
= [&](auto item) {
p::match(node.value().interface)(
pattern("org.tuxclocker.Assignable") =
[=, &rowItems] {
if_let(pattern(some(arg)) =
setupAssignable(node, conn)) = [&](auto item) {
nameItem->setData(Assignable, InterfaceTypeRole);
auto icon = assignableIcon();
nameItem->setData(icon, Qt::DecorationRole);
rowItems.append(item);
};
},
pattern("org.tuxclocker.DynamicReadable") = [=, &rowItems] {
if_let(pattern(some(arg)) = setupDynReadable(node, conn))
= [&](auto item) {
pattern("org.tuxclocker.DynamicReadable") =
[=, &rowItems] {
if_let(pattern(some(arg)) =
setupDynReadable(node, conn)) = [&](auto item) {
auto icon = dynamicReadableIcon();
nameItem->setData(icon, Qt::DecorationRole);
nameItem->setData(DeviceModel::DynamicReadable,
InterfaceTypeRole);
nameItem->setData(icon, Qt::DecorationRole);
nameItem->setData(
DeviceModel::DynamicReadable, InterfaceTypeRole);
rowItems.append(item);
//qDebug() << item->data(DynamicReadableProxyRole);
// qDebug() << item->data(DynamicReadableProxyRole);
};
},
pattern("org.tuxclocker.StaticReadable") = [=, &rowItems] {
if_let(pattern(some(arg)) = setupStaticReadable(node, conn))
= [&](auto item) {
pattern("org.tuxclocker.StaticReadable") =
[=, &rowItems] {
if_let(pattern(some(arg)) =
setupStaticReadable(node, conn)) = [&](auto item) {
auto icon = staticReadableIcon();
nameItem->setData(icon, Qt::DecorationRole);
nameItem->setData(DeviceModel::StaticReadable,
InterfaceTypeRole);
nameItem->setData(
DeviceModel::StaticReadable, InterfaceTypeRole);
rowItems.append(item);
};
},
pattern(_) = []{}
);
pattern(_) = [] {});
item->appendRow(rowItems);
for (auto c_node : node.children())
traverse(c_node, nameItem);
};
auto rootItem = invisibleRootItem();
for (auto &node : root.children())
traverse(node, rootItem);
}
EnumerationVec toEnumVec(QVector<TCDBus::Enumeration> enums) {
std::vector<TCDBus::Enumeration> stdEnumVec(enums.begin(), enums.end());
return transform([](auto e) {
return Enumeration{e.name.toStdString(), e.key};
}, stdEnumVec);
return transform(
[](auto e) {
return Enumeration{e.name.toStdString(), e.key};
},
stdEnumVec);
}
std::optional<const AssignableProxy*>
DeviceModel::assignableProxyFromItem(QStandardItem *item) {
return (m_assignableProxyHash.contains(item)) ?
std::optional(m_assignableProxyHash.value(item)) :
std::nullopt;
std::optional<const AssignableProxy *> DeviceModel::assignableProxyFromItem(QStandardItem *item) {
return (m_assignableProxyHash.contains(item))
? std::optional(m_assignableProxyHash.value(item))
: std::nullopt;
}
QString fromAssignmentArgument(AssignmentArgument a_arg) {
return p::match(a_arg) (
pattern(as<int>(arg)) = [](auto i) {return QString::number(i);},
pattern(as<uint>(arg)) = [](auto u) {return QString::number(u);},
pattern(as<double>(arg)) = [](auto d) {return QString::number(d);},
pattern(_) = [] {return QString("");}
);
return p::match(a_arg)(
pattern(as<int>(arg)) = [](auto i) { return QString::number(i); },
pattern(as<uint>(arg)) = [](auto u) { return QString::number(u); },
pattern(as<double>(arg)) = [](auto d) { return QString::number(d); },
pattern(_) = [] { return QString(""); });
}
QStandardItem *DeviceModel::createAssignable(TC::TreeNode<TCDBus::DeviceNode> node,
QDBusConnection conn, AssignableItemData itemData) {
QStandardItem *DeviceModel::createAssignable(
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn, AssignableItemData itemData) {
auto ifaceItem = new AssignableItem(this);
auto proxy = new AssignableProxy(node.value().path, conn, this);
connect(proxy, &AssignableProxy::connectionValueChanged, [=] (auto result, auto text) {
p::match(result) (
pattern(as<QVariant>(arg)) = [=](auto v) {
connect(proxy, &AssignableProxy::connectionValueChanged, [=](auto result, auto text) {
p::match(result)(
pattern(as<QVariant>(arg)) =
[=](auto v) {
QVariant data;
data.setValue(connectionColor());
ifaceItem->setData(data, Qt::BackgroundRole);
ifaceItem->setText(text);
//qDebug() << text;
// qDebug() << text;
},
pattern(_) = []{}
);
pattern(_) = [] {});
});
QVariant pv;
pv.setValue(proxy);
ifaceItem->setData(pv, AssignableProxyRole);
@@ -151,11 +151,12 @@ QStandardItem *DeviceModel::createAssignable(TC::TreeNode<TCDBus::DeviceNode> no
auto currentValue = proxy->currentValue();
if (currentValue.has_value()) {
p::match(itemData.assignableInfo()) (
pattern(as<EnumerationVec>(arg)) = [&](auto e_vec) {
/* Find index from EnumVec (O(n) doesn't matter since we only search once here)
This should never be anything other than an uint but in theory it could be
whatever at this point */
p::match(itemData.assignableInfo())(
pattern(as<EnumerationVec>(arg)) =
[&](auto e_vec) {
/* Find index from EnumVec (O(n) doesn't matter since we only search
once here) This should never be anything other than an uint but
in theory it could be whatever at this point */
try {
auto index = std::get<uint>(currentValue.value());
for (auto &e : e_vec) {
@@ -164,58 +165,58 @@ QStandardItem *DeviceModel::createAssignable(TC::TreeNode<TCDBus::DeviceNode> no
break;
}
}
} catch (std::bad_variant_access &e) {}
} catch (std::bad_variant_access &e) {
}
//text = QString::fromStdString(e_vec[std::get<uint>(currentValue.value())].name);
// text =
// QString::fromStdString(e_vec[std::get<uint>(currentValue.value())].name);
},
pattern(as<RangeInfo>(_)) = [&]() {
pattern(as<RangeInfo>(_)) =
[&]() {
auto base = fromAssignmentArgument(currentValue.value());
if (unit.has_value())
text = QString("%1 %2").arg(base, unit.value());
else
text = base;
}
);
});
}
ifaceItem->setText(text);
connect(ifaceItem, &AssignableItem::assignableDataChanged,
[=](QVariant v) {
connect(ifaceItem, &AssignableItem::assignableDataChanged, [=](QVariant v) {
// Only show checkbox when value has been changed
ifaceItem->setCheckable(true);
ifaceItem->setCheckState(Qt::Checked);
proxy->setValue(v);
ifaceItem->setData(unappliedColor(), Qt::BackgroundRole);
});
connect(ifaceItem, &AssignableItem::committalChanged, [=](bool on) {
QVariant colorData = (on) ? unappliedColor() : QVariant();
ifaceItem->setData(colorData, Qt::BackgroundRole);
});
connect(proxy, &AssignableProxy::applied, [=](auto err) {
// Fade out result color
auto startColor = (err.has_value()) ? errorColor()
: successColor();
auto startColor = (err.has_value()) ? errorColor() : successColor();
auto anim = new QVariantAnimation;
anim->setDuration(fadeOutTime());
anim->setStartValue(startColor);
anim->setEndValue(QPalette().color(QPalette::Base));
connect(anim, &QVariantAnimation::valueChanged, [=](QVariant v) {
QVariant iv;
iv.setValue(v.value<QColor>());
ifaceItem->setData(iv, Qt::BackgroundRole);
});
connect(anim, &QVariantAnimation::finished, [=] {
// Set invalid color to 'reset' the color
ifaceItem->setData(QVariant(), Qt::BackgroundRole);
});
anim->start(QAbstractAnimation::DeleteWhenStopped);
});
connect(this, &DeviceModel::changesApplied, [=] {
// Don't apply if unchecked
if (ifaceItem->checkState() == Qt::Checked) {
@@ -232,13 +233,11 @@ QStandardItem *DeviceModel::createAssignable(TC::TreeNode<TCDBus::DeviceNode> no
QVariant DeviceModel::data(const QModelIndex &index, int role) const {
if (index.row() != DeviceModel::NameColumn && role == DeviceModel::NodeNameRole) {
// Get name from adjacent column
auto nameIndex =
this->index(index.row(), DeviceModel::NameColumn, index.parent());
auto nameIndex = this->index(index.row(), DeviceModel::NameColumn, index.parent());
return nameIndex.data(Qt::DisplayRole);
}
if (index.column() != InterfaceColumn && role == DynamicReadableProxyRole) {
auto idx =
this->index(index.row(), DeviceModel::InterfaceColumn, index.parent());
auto idx = this->index(index.row(), DeviceModel::InterfaceColumn, index.parent());
return idx.data(DynamicReadableProxyRole);
}
return QStandardItemModel::data(index, role);
@@ -250,44 +249,38 @@ std::optional<QString> fromDBusResult(TCDBus::Result<QString> res) {
return res.value;
}
std::optional<QStandardItem*> DeviceModel::setupAssignable(
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn) {
QDBusInterface ifaceNode("org.tuxclocker", node.value().path,
"org.tuxclocker.Assignable", conn);
std::optional<QStandardItem *> DeviceModel::setupAssignable(
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn) {
QDBusInterface ifaceNode(
"org.tuxclocker", node.value().path, "org.tuxclocker.Assignable", conn);
// Calling QObject::property used to work for getting (some) DBus properties but now we have to do this..
QDBusInterface propIface{"org.tuxclocker", node.value().path,
"org.freedesktop.DBus.Properties", conn};
QDBusReply<QDBusVariant> a_reply = propIface.call("Get", "org.tuxclocker.Assignable", "assignableInfo");
QDBusReply<QDBusVariant> u_reply = propIface.call("Get", "org.tuxclocker.Assignable", "unit");
// Calling QObject::property used to work for getting (some) DBus properties but now we have
// to do this..
QDBusInterface propIface{
"org.tuxclocker", node.value().path, "org.freedesktop.DBus.Properties", conn};
QDBusReply<QDBusVariant> a_reply =
propIface.call("Get", "org.tuxclocker.Assignable", "assignableInfo");
QDBusReply<QDBusVariant> u_reply =
propIface.call("Get", "org.tuxclocker.Assignable", "unit");
if (!a_reply.isValid() || !u_reply.isValid()) {
// This code path shouldn't be reached
// The DBus path is contained in the error message
qWarning("Could not get assignableInfo or unit for Assignable "
"due to error(s) \"%s\" and \"%s\"",
qPrintable(a_reply.error().message()),
qPrintable(u_reply.error().message()));
"due to error(s) \"%s\" and \"%s\"",
qPrintable(a_reply.error().message()), qPrintable(u_reply.error().message()));
return std::nullopt;
}
// Calling QDBusReply::value() should ge safe here
// Need to serialize manually into the proper types
auto unit =
fromDBusResult(
qdbus_cast<TCDBus::Result<QString>>(u_reply
.value()
.variant()
.value<QDBusArgument>()));
auto unit = fromDBusResult(
qdbus_cast<TCDBus::Result<QString>>(u_reply.value().variant().value<QDBusArgument>()));
// What a mess...
auto assInfoRaw = a_reply
.value()
.variant()
.value<QDBusVariant>()
.variant()
.value<QDBusArgument>();
auto assInfoRaw =
a_reply.value().variant().value<QDBusVariant>().variant().value<QDBusArgument>();
// TODO: get initial value
// Use DBus signature to know the type of Assignable
if (assInfoRaw.currentSignature() == "(vv)") {
// RangeInfo
@@ -304,69 +297,62 @@ std::optional<QStandardItem*> DeviceModel::setupAssignable(
}
template <typename T>
void updateReadItemText(QStandardItem *item, T value,
std::optional<QString> unit) {
void updateReadItemText(QStandardItem *item, T value, std::optional<QString> unit) {
// TODO: this can be made a lot (around 3x) faster by using direct copying
// Form a string of the form "1000 MHz" if has unit
auto text = (unit.has_value()) ?
QString("%1 %2").arg(value).arg(unit.value()) :
QString("%1").arg(value);
auto text = (unit.has_value()) ? QString("%1 %2").arg(value).arg(unit.value())
: QString("%1").arg(value);
item->setText(text);
//qDebug() << item->data(DeviceModel::DynamicReadableProxyRole);
// qDebug() << item->data(DeviceModel::DynamicReadableProxyRole);
}
std::optional<QStandardItem*> DeviceModel::setupDynReadable(
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn) {
std::optional<QStandardItem *> DeviceModel::setupDynReadable(
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn) {
auto item = new QStandardItem;
auto proxy = new DynamicReadableProxy(node.value().path, conn, this);
QVariant v;
v.setValue(proxy);
item->setData(v, DynamicReadableProxyRole);
auto unit = proxy->unit();
auto unit = proxy->unit();
connect(proxy, &DynamicReadableProxy::valueChanged, [=](ReadResult res) {
p::match(res)(
pattern(as<ReadableValue>(arg)) = [=](auto rv) {
pattern(as<ReadableValue>(arg)) =
[=](auto rv) {
p::match(rv)(
pattern(as<double>(arg)) = [=](auto d) {
updateReadItemText(item, d, unit);
},
pattern(as<int>(arg)) = [=](auto i) {
updateReadItemText(item, i, unit);
},
pattern(as<uint>(arg)) = [=](auto u) {
updateReadItemText(item, u, unit);
},
pattern(_) = []{}
);
pattern(as<double>(arg)) =
[=](auto d) { updateReadItemText(item, d, unit); },
pattern(as<int>(arg)) =
[=](auto i) { updateReadItemText(item, i, unit); },
pattern(as<uint>(arg)) =
[=](auto u) { updateReadItemText(item, u, unit); },
pattern(_) = [] {});
},
pattern(_) = []{}
);
pattern(_) = [] {});
});
return item;
}
std::optional<QStandardItem*> DeviceModel::setupStaticReadable(
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn) {
QDBusInterface staticIface("org.tuxclocker", node.value().path,
"org.tuxclocker.StaticReadable", conn);
auto value = staticIface.property("value")
.value<QDBusVariant>().variant().toString();
std::optional<QStandardItem *> DeviceModel::setupStaticReadable(
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn) {
QDBusInterface staticIface(
"org.tuxclocker", node.value().path, "org.tuxclocker.StaticReadable", conn);
auto value = staticIface.property("value").value<QDBusVariant>().variant().toString();
// Workaround from DynamicReadableProxy for getting property with custom type
QDBusInterface propIface("org.tuxclocker", node.value().path,
"org.freedesktop.DBus.Properties", conn);
QDBusInterface propIface(
"org.tuxclocker", node.value().path, "org.freedesktop.DBus.Properties", conn);
QDBusReply<QDBusVariant> reply =
propIface.call("Get", "org.tuxclocker.StaticReadable", "unit");
propIface.call("Get", "org.tuxclocker.StaticReadable", "unit");
if (!reply.isValid())
return std::nullopt;
auto arg = reply.value().variant().value<QDBusArgument>();
TCDBus::Result<QString> unit;
arg >> unit;
if (!unit.error)
value += " " + unit.value;
auto item = new QStandardItem;
item->setData(value, Qt::DisplayRole);
return item;

View File

@@ -27,19 +27,19 @@ class DeviceModel : public QStandardItemModel {
public:
DeviceModel(TC::TreeNode<TCDBus::DeviceNode> root, QObject *parent = nullptr);
enum ColumnType {
NameColumn = 0, // Node name
NameColumn = 0, // Node name
InterfaceColumn = 1 // Column for presenting interfaces
};
enum Role {
AssignableRole = Qt::UserRole, // Holds the data about the assignable
AssignableProxyRole,
ConnectionRole, // Data about the connection
DynamicReadableProxyRole,
InterfaceTypeRole, // InterfaceType
NodeNameRole //
NodeNameRole //
};
enum InterfaceFlag {
Assignable = 1,
DynamicReadable = 2,
@@ -49,36 +49,36 @@ public:
typedef QFlags<InterfaceFlag> InterfaceFlags;
// For decoupling AssignableItems created in the model
void applyChanges() {emit changesApplied();}
void applyChanges() { emit changesApplied(); }
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
static QIcon assignableIcon() {return QIcon::fromTheme("edit-entry");}
static QIcon assignableIcon() { return QIcon::fromTheme("edit-entry"); }
// Get AssignableProxy of an item
std::optional<const AssignableProxy*> assignableProxyFromItem(QStandardItem *item);
std::optional<const AssignableProxy *> assignableProxyFromItem(QStandardItem *item);
// AssignableProxies are associated with both row items
//QVariant data(QModelIndex&);
static QIcon dynamicReadableIcon() {return QIcon(":/ruler.svg");}
static QIcon staticReadableIcon() {return QIcon::fromTheme("help-about");}
// QVariant data(QModelIndex&);
static QIcon dynamicReadableIcon() { return QIcon(":/ruler.svg"); }
static QIcon staticReadableIcon() { return QIcon::fromTheme("help-about"); }
signals:
void changesApplied();
private:
Q_OBJECT
QHash<QStandardItem*, AssignableProxy*> m_assignableProxyHash;
QHash<QStandardItem *, AssignableProxy *> m_assignableProxyHash;
// Separate handling interfaces since otherwise we run out of columns
QStandardItem *createAssignable(TC::TreeNode<TCDBus::DeviceNode> node,
QDBusConnection conn, AssignableItemData data);
std::optional<QStandardItem*> setupAssignable(
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn);
std::optional<QStandardItem*> setupDynReadable(
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn);
std::optional<QStandardItem*> setupStaticReadable(
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn);
constexpr int fadeOutTime() {return 5000;} // milliseconds
constexpr int transparency() {return 120;} // 0-255
QStandardItem *createAssignable(
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn, AssignableItemData data);
std::optional<QStandardItem *> setupAssignable(
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn);
std::optional<QStandardItem *> setupDynReadable(
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn);
std::optional<QStandardItem *> setupStaticReadable(
TC::TreeNode<TCDBus::DeviceNode> node, QDBusConnection conn);
constexpr int fadeOutTime() { return 5000; } // milliseconds
constexpr int transparency() { return 120; } // 0-255
// Colors for items
QColor connectionColor() {return QColor(0, 0, 255, transparency());} // blue
QColor errorColor() {return QColor(255, 0, 0, transparency());} // red
QColor unappliedColor() {return QColor(255, 255, 0, transparency());} // yellow
QColor successColor() {return QColor(0, 255, 0, transparency());} // green
QColor connectionColor() { return QColor(0, 0, 255, transparency()); } // blue
QColor errorColor() { return QColor(255, 0, 0, transparency()); } // red
QColor unappliedColor() { return QColor(255, 255, 0, transparency()); } // yellow
QColor successColor() { return QColor(0, 255, 0, transparency()); } // green
};

View File

@@ -13,66 +13,61 @@ using namespace mpark::patterns;
Q_DECLARE_METATYPE(AssignableItemData)
DeviceModelDelegate::DeviceModelDelegate(QObject* parent) :
QStyledItemDelegate(parent) {}
QWidget *DeviceModelDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem&, const QModelIndex &index) const {
DeviceModelDelegate::DeviceModelDelegate(QObject *parent) : QStyledItemDelegate(parent) {}
QWidget *DeviceModelDelegate::createEditor(
QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &index) const {
auto v = index.data(DeviceModel::AssignableRole);
QWidget *editor = nullptr;
if (v.canConvert<AssignableItemData>()) {
match(v.value<AssignableItemData>().assignableInfo())
(pattern(as<RangeInfo>(arg)) = [&](auto r_info) {
match(r_info)
(pattern(as<Range<int>>(arg)) = [&](auto ir) {
editor = new IntRangeEditor(ir, parent);
},
pattern(as<Range<double>>(arg)) = [&](auto dr) {
match(v.value<AssignableItemData>().assignableInfo())(
pattern(as<RangeInfo>(arg)) =
[&](auto r_info) {
match(r_info)(
pattern(as<Range<int>>(arg)) =
[&](auto ir) { editor = new IntRangeEditor(ir, parent); },
pattern(as<Range<double>>(arg)) =
[&](auto dr) {
editor = new DoubleRangeEditor(dr, parent);
}
);
});
},
pattern(as<EnumerationVec>(arg)) = [&](auto ev) {
editor = new EnumEditor(ev, parent);
}
);
pattern(as<EnumerationVec>(arg)) =
[&](auto ev) { editor = new EnumEditor(ev, parent); });
}
return editor;
}
void DeviceModelDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const {
void DeviceModelDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const {
auto v = index.data(DeviceModel::AssignableRole);
if (v.canConvert<AssignableItemData>()) {
auto data = v.value<AssignableItemData>().value();
auto a_editor = static_cast<AbstractAssignableEditor*>(editor);
auto a_editor = static_cast<AbstractAssignableEditor *>(editor);
a_editor->setAssignableData(data);
}
}
void DeviceModelDelegate::setModelData(QWidget *editor,
QAbstractItemModel *model, const QModelIndex &index) const {
void DeviceModelDelegate::setModelData(
QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const {
auto v = index.data(DeviceModel::AssignableRole);
if (v.canConvert<AssignableItemData>()) {
auto a_editor = static_cast<AbstractAssignableEditor*>(editor);
auto data = index.data(DeviceModel::AssignableRole)
.value<AssignableItemData>();
auto a_editor = static_cast<AbstractAssignableEditor *>(editor);
auto data = index.data(DeviceModel::AssignableRole).value<AssignableItemData>();
data.setValue(a_editor->assignableData());
QVariant v;
v.setValue(data);
// TODO: add unit
auto text = (data.unit().has_value()) ?
QString("%1 %2").arg(a_editor->displayData(), data.unit().value()) :
a_editor->displayData();
auto text = (data.unit().has_value())
? QString("%1 %2").arg(a_editor->displayData(), data.unit().value())
: a_editor->displayData();
model->setData(index, text, Qt::DisplayRole);
model->setData(index, v, DeviceModel::AssignableRole);
}
}
void DeviceModelDelegate::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex&) const {
void DeviceModelDelegate::updateEditorGeometry(
QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &) const {
// Why do I need to override this to perform such a basic task?
editor->setGeometry(option.rect);
}
@@ -80,42 +75,36 @@ void DeviceModelDelegate::updateEditorGeometry(QWidget *editor,
QColor alphaBlend(QColor top, QColor background) {
auto alpha = top.alphaF();
auto factor = 1 - alpha;
return QColor(
(top.red() * alpha) + (factor * background.red()),
(top.green() * alpha) + (factor * background.green()),
(top.blue() * alpha) + (factor * background.blue())
);
return QColor((top.red() * alpha) + (factor * background.red()),
(top.green() * alpha) + (factor * background.green()),
(top.blue() * alpha) + (factor * background.blue()));
}
QColor filter(QColor filter, QColor color) {
/* Dominant color lets 95% (in case of a theme using pure r/g/b) of that
QColor filter(QColor filter, QColor color) {
/* Dominant color lets 95% (in case of a theme using pure r/g/b) of that
channel through, the rest 70% */
auto factorR = filter.redF();
auto factorG = filter.greenF();
auto factorB = filter.blueF();
Qt::GlobalColor dominant;
if (factorR > qMax(factorG, factorB))
dominant = Qt::red;
if (factorG > qMax(factorR, factorB))
dominant = Qt::green;
if (factorB > qMax(factorR, factorG))
dominant = Qt::blue;
factorR = (dominant == Qt::red) ? 0.95 : 0.7;
factorG = (dominant == Qt::green) ? 0.95 : 0.7;
factorB = (dominant == Qt::blue) ? 0.95 : 0.7;
return QColor(
color.red() * factorR,
color.green() * factorG,
color.blue() * factorB
);
return QColor(color.red() * factorR, color.green() * factorG, color.blue() * factorB);
}
void DeviceModelDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const {
void DeviceModelDelegate::paint(
QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
auto optCpy = option;
auto baseColor = index.data(Qt::BackgroundRole).value<QColor>();
if (baseColor.isValid()) {

View File

@@ -6,14 +6,14 @@
class DeviceModelDelegate : public QStyledItemDelegate {
public:
DeviceModelDelegate(QObject *parent = nullptr);
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
const QModelIndex &index) const;
QWidget *createEditor(
QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void updateEditorGeometry(
QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void setEditorData(QWidget *editor, const QModelIndex &index) const;
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const;
void setModelData(
QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;
protected:
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
const QModelIndex &index) const override;
};

View File

@@ -4,20 +4,18 @@
Q_DECLARE_METATYPE(DeviceModel::InterfaceFlag)
DeviceProxyModel::DeviceProxyModel(DeviceModel &model, QObject *parent) :
QSortFilterProxyModel(parent), m_disableFiltered(false),
m_showIcons(true), m_showValueColumn(true) {
DeviceProxyModel::DeviceProxyModel(DeviceModel &model, QObject *parent)
: QSortFilterProxyModel(parent), m_disableFiltered(false), m_showIcons(true),
m_showValueColumn(true) {
setSourceModel(&model);
m_flags = DeviceModel::AllInterfaces;
}
bool DeviceProxyModel::filterAcceptsRow(int sourceRow,
const QModelIndex &sourceParent) const {
bool DeviceProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
auto model = sourceModel();
// Interface type is stored in the item with the name
auto thisItem = model->index(sourceRow, DeviceModel::NameColumn,
sourceParent);
auto thisItem = model->index(sourceRow, DeviceModel::NameColumn, sourceParent);
// Check recursively if a child item has the flag set that we want to show
bool shouldHide = true;
std::function<void(QModelIndex)> traverse;
@@ -41,21 +39,18 @@ bool DeviceProxyModel::filterAcceptsRow(int sourceRow,
Qt::ItemFlags DeviceProxyModel::flags(const QModelIndex &index) const {
if (!m_disableFiltered)
return QSortFilterProxyModel::flags(index);
auto iface =
QSortFilterProxyModel::data(index, DeviceModel::InterfaceTypeRole);
auto iface = QSortFilterProxyModel::data(index, DeviceModel::InterfaceTypeRole);
auto removedFlags = Qt::ItemIsSelectable;
auto itemFlags = QSortFilterProxyModel::flags(index);
if (!iface.isValid())
// Remove selectable flag
return itemFlags &(~removedFlags);
return itemFlags & (~removedFlags);
auto flags = iface.value<DeviceModel::InterfaceFlag>();
return (flags & m_flags) ? itemFlags :
itemFlags &(~removedFlags);
return (flags & m_flags) ? itemFlags : itemFlags & (~removedFlags);
}
bool DeviceProxyModel::lessThan(const QModelIndex &left,
const QModelIndex &right) const {
bool DeviceProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const {
// TODO: doesn't work as expected if sorted from interface column
auto leftChildren = sourceModel()->rowCount(left);
auto rightChildren = sourceModel()->rowCount(right);

View File

@@ -8,9 +8,9 @@
class DeviceProxyModel : public QSortFilterProxyModel {
public:
DeviceProxyModel(DeviceModel &model, QObject *parent = nullptr);
DeviceModel::InterfaceFlags filterFlags() {return m_flags;}
DeviceModel::InterfaceFlags filterFlags() { return m_flags; }
// Disable items that don't contain the interface in m_flags
void setDisableFiltered(bool on) {m_disableFiltered = on;}
void setDisableFiltered(bool on) { m_disableFiltered = on; }
void setFlags(DeviceModel::InterfaceFlags flags) {
m_flags = flags;
invalidateFilter();
@@ -27,16 +27,13 @@ public:
invalidateFilter();
}
protected:
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole)
const override {
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override {
if (!m_showIcons && role == Qt::DecorationRole)
return QVariant();
return QSortFilterProxyModel::data(index, role);
}
bool filterAcceptsRow(int sourceRow,
const QModelIndex &sourceParent) const override;
bool filterAcceptsColumn(int sourceColumn, const QModelIndex&)
const override {
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
bool filterAcceptsColumn(int sourceColumn, const QModelIndex &) const override {
return !(!m_showValueColumn && sourceColumn == DeviceModel::InterfaceColumn);
}
// Optionally disable selection of items that don't have the wanted interface

View File

@@ -25,20 +25,19 @@ public:
QString dynamicReadablePath;
};
// TODO: make DynamicReadableProxy type parametrized so we can be sure to only get numeric values from it
// Connection of an Assignable with a DynamicReadable
// TODO: make DynamicReadableProxy type parametrized so we can be sure to only get numeric values
// from it Connection of an Assignable with a DynamicReadable
template <typename OutType> // Result of linear interpolation
class DynamicReadableConnection : public AssignableConnection {
public:
DynamicReadableConnection(DynamicReadableProxy &proxy,
QVector<QPointF> points, QObject *parent = nullptr)
: AssignableConnection(parent), m_proxy(proxy), m_points(points) {
auto sorted = sort_by([](auto point_l, auto point_r) {
return point_l.x() < point_r.x();
}, m_points);
m_points = sorted;
}
virtual QVariant connectionData() override {return QVariant();}
DynamicReadableConnection(
DynamicReadableProxy &proxy, QVector<QPointF> points, QObject *parent = nullptr)
: AssignableConnection(parent), m_proxy(proxy), m_points(points) {
auto sorted = sort_by(
[](auto point_l, auto point_r) { return point_l.x() < point_r.x(); }, m_points);
m_points = sorted;
}
virtual QVariant connectionData() override { return QVariant(); }
virtual void start() override {
/*QObject::connect(&m_timer, &QTimer::timeout, [this] {
int rand = QRandomGenerator::global()->bounded(0, 10);
@@ -51,34 +50,27 @@ public:
m_timer.start(1000);*/
connect(&m_proxy, &DynamicReadableProxy::valueChanged, [this](auto val) {
match(val) (
pattern(as<ReadableValue>(arg)) = [this](auto rv) {
match(rv) (
pattern(as<uint>(arg)) = [this](auto u) {
emitTargetValue(u);
}
);
}
);
match(val)(pattern(as<ReadableValue>(arg)) = [this](auto rv) {
match(rv)(pattern(as<uint>(arg)) = [this](auto u) {
emitTargetValue(u);
});
});
});
/*QDBusVariant arg{QVariant{1}};
QVariant v;
v.setValue(arg);
emit targetValueChanged(v, "1");*/
}
virtual void stop() override {
}
virtual void stop() override {}
private:
DynamicReadableProxy &m_proxy;
QTimer m_timer;
QVector<QPointF> m_points;
template <typename U>
void emitTargetValue(U reading) {
template <typename U> void emitTargetValue(U reading) {
// Find two points from the vector so that:
// p[i].x < val < p[i + 1].x
// p[i].x < val < p[i + 1].x
std::optional<int> leftIndex = std::nullopt;
for (int i = 0; i < m_points.length() - 1; i++) {
if (m_points[i].x() < reading && reading < m_points[i + 1].x()) {
@@ -93,14 +85,12 @@ private:
double dx = m_points[li + 1].x() - m_points[li].x();
double dvx = reading - m_points[li].x();
double p = dvx / dx;
OutType interp_y = lerp(m_points[li].y(),
m_points[li + 1].y(), p);
OutType interp_y = lerp(m_points[li].y(), m_points[li + 1].y(), p);
QDBusVariant arg{QVariant{interp_y}};
QVariant v;
v.setValue(arg);
emit targetValueChanged(v, QString::number(interp_y));
}
template <typename T>
T lerp(T a, T b, double t) {return a + (t * (b - a));}
template <typename T> T lerp(T a, T b, double t) { return a + (t * (b - a)); }
};

View File

@@ -14,38 +14,37 @@ Q_DECLARE_METATYPE(TCD::Result<QString>)
ReadResult toTCResult(TCD::Result<QDBusVariant> res) {
if (res.error)
return static_cast<ReadError>(res.value.variant().toInt());
auto type = static_cast<QMetaType::Type>(res.value.variant().type());
auto v = res.value.variant();
switch (type) {
case QMetaType::Int:
return v.value<int>();
case QMetaType::UInt:
return v.value<uint>();
case QMetaType::Double:
return v.value<double>();
default:
// TODO: indicate unhandled value
return ReadError::UnknownError;
case QMetaType::Int:
return v.value<int>();
case QMetaType::UInt:
return v.value<uint>();
case QMetaType::Double:
return v.value<double>();
default:
// TODO: indicate unhandled value
return ReadError::UnknownError;
}
}
DynamicReadableProxy::DynamicReadableProxy(QString path, QDBusConnection conn,
QObject *parent) : QObject(parent),
m_iface("org.tuxclocker", path, "org.tuxclocker.DynamicReadable", conn) {
DynamicReadableProxy::DynamicReadableProxy(QString path, QDBusConnection conn, QObject *parent)
: QObject(parent), m_iface("org.tuxclocker", path, "org.tuxclocker.DynamicReadable", conn) {
qDBusRegisterMetaType<TCD::Result<QDBusVariant>>();
qDBusRegisterMetaType<TCD::Result<QString>>();
m_timer.start(1000);
connect(&m_timer, &QTimer::timeout, [=] {
QDBusReply<TCD::Result<QDBusVariant>> reply = m_iface.call("value");
if (!reply.isValid())
emit valueChanged(ReadError::UnknownError);
else
//qDebug() << QVariant::fromStdVariant(toTCResult(reply.value()));
// qDebug() << QVariant::fromStdVariant(toTCResult(reply.value()));
emit valueChanged(toTCResult(reply.value()));
});
}
@@ -54,9 +53,9 @@ std::optional<QString> DynamicReadableProxy::unit() {
/* Workaround for QVariant, or whatever errors out braindeath by calling
the method instead */
QDBusInterface propIface("org.tuxclocker", m_iface.path(),
"org.freedesktop.DBus.Properties", m_iface.connection());
"org.freedesktop.DBus.Properties", m_iface.connection());
QDBusReply<QDBusVariant> reply =
propIface.call("Get", "org.tuxclocker.DynamicReadable", "unit");
propIface.call("Get", "org.tuxclocker.DynamicReadable", "unit");
if (!reply.isValid()) {
return std::nullopt;
}

View File

@@ -8,14 +8,13 @@
class DynamicReadableProxy : public QObject {
public:
DynamicReadableProxy(QString path, QDBusConnection conn,
QObject *parent = nullptr);
DynamicReadableProxy(QString path, QDBusConnection conn, QObject *parent = nullptr);
std::optional<QString> unit();
signals:
void valueChanged(TuxClocker::Device::ReadResult val);
private:
Q_OBJECT
QDBusInterface m_iface;
QTimer m_timer; // Emits latest value
};

View File

@@ -7,6 +7,6 @@ int main(int argc, char **argv) {
MainWindow mw;
mw.show();
return app.exec();
}

View File

@@ -9,10 +9,10 @@ using namespace mpark::patterns;
using namespace TuxClocker::Device;
Q_DECLARE_METATYPE(AssignableItemData)
Q_DECLARE_METATYPE(AssignableProxy*)
Q_DECLARE_METATYPE(AssignableProxy *)
DeviceBrowser::DeviceBrowser(DeviceModel &model, QWidget *parent)
: QWidget(parent), m_deviceModel(model) {
DeviceBrowser::DeviceBrowser(DeviceModel &model, QWidget *parent)
: QWidget(parent), m_deviceModel(model) {
m_layout = new QGridLayout(this);
m_proxyModel = new DeviceProxyModel(model, this);
m_treeView = new DeviceTreeView;
@@ -25,50 +25,36 @@ DeviceBrowser::DeviceBrowser(DeviceModel &model, QWidget *parent)
m_flagLabel = new QLabel("Showing:");
m_apply = new QPushButton("Apply changes");
m_apply->setEnabled(true);
m_flagEditor = new FlagEditor(
QVector({
std::tuple(
QString("Assignables"),
DeviceModel::assignableIcon(),
DeviceModel::Assignable
),
std::tuple(
QString("Dynamic Values"),
DeviceModel::dynamicReadableIcon(),
DeviceModel::DynamicReadable
),
std::tuple(
QString("Static Values"),
DeviceModel::staticReadableIcon(),
DeviceModel::StaticReadable
)
}), this);
connect(m_apply, &QPushButton::pressed, &m_deviceModel,
&DeviceModel::applyChanges);
QVector({std::tuple(QString("Assignables"), DeviceModel::assignableIcon(),
DeviceModel::Assignable),
std::tuple(QString("Dynamic Values"), DeviceModel::dynamicReadableIcon(),
DeviceModel::DynamicReadable),
std::tuple(QString("Static Values"), DeviceModel::staticReadableIcon(),
DeviceModel::StaticReadable)}),
this);
connect(m_apply, &QPushButton::pressed, &m_deviceModel, &DeviceModel::applyChanges);
m_treeView->functionEditorRequested.connect([this](QModelIndex &index) {
auto a_data = index.data(DeviceModel::AssignableRole);
if (!a_data.isValid())
return;
auto a_info = a_data.value<AssignableItemData>();
auto proxy = index.data(DeviceModel::AssignableProxyRole)
.value<AssignableProxy*>();
auto proxy =
index.data(DeviceModel::AssignableProxyRole).value<AssignableProxy *>();
auto name = index.data(DeviceModel::NodeNameRole).toString();
match(a_info.assignableInfo()) (
pattern(as<RangeInfo>(arg)) = [=](auto ri) {
auto f_editor = new FunctionEditor{m_deviceModel,
ri, *proxy, name};
match(a_info.assignableInfo())(
pattern(as<RangeInfo>(arg)) =
[=](auto ri) {
auto f_editor = new FunctionEditor{m_deviceModel, ri, *proxy, name};
f_editor->show();
f_editor->assignableConnectionChanged
.connect([=](auto conn) {
proxy->startConnection(conn);
});
f_editor->assignableConnectionChanged.connect(
[=](auto conn) { proxy->startConnection(conn); });
},
pattern(_) = [] {}
);
pattern(_) = [] {});
/*auto f_editor = new FunctionEditor(m_deviceModel, rangeInfo, proxy);
f_editor->show();
@@ -78,17 +64,15 @@ DeviceBrowser::DeviceBrowser(DeviceModel &model, QWidget *parent)
proxy.startConnection(assignableConnection);
});*/
});
m_flagEditor->setFlags(DeviceModel::AllInterfaces);
m_flagEditor->flagsChanged.connect([=](auto flags) {
m_proxyModel->setFlags(flags);
});
m_flagEditor->flagsChanged.connect([=](auto flags) { m_proxyModel->setFlags(flags); });
m_layout->addWidget(m_flagLabel, 0, 0);
m_layout->addWidget(m_flagEditor, 0, 1);
m_layout->addWidget(m_treeView, 1, 0, 1, 2);
m_layout->addWidget(m_apply, 2, 0, 1, 2);
setLayout(m_layout);
}

View File

@@ -11,10 +11,9 @@ using namespace mpark::patterns;
using namespace TuxClocker::Device;
Q_DECLARE_METATYPE(AssignableItemData)
Q_DECLARE_METATYPE(AssignableProxy*)
Q_DECLARE_METATYPE(AssignableProxy *)
DeviceTreeView::DeviceTreeView(QWidget *parent)
: QTreeView(parent) {
DeviceTreeView::DeviceTreeView(QWidget *parent) : QTreeView(parent) {
header()->setSectionResizeMode(QHeaderView::ResizeToContents);
setSortingEnabled(true);
setEditTriggers(SelectedClicked | EditKeyPressed);
@@ -30,12 +29,13 @@ DeviceTreeView::DeviceTreeView(QWidget *parent)
enableConn->setDefaultWidget(&checkBox);
menu.addActions({&editConn, enableConn});
if (data.canConvert<AssignableItemData>() &&
proxyData.canConvert<AssignableProxy*>()) {
proxyData.canConvert<AssignableProxy *>()) {
functionEditorRequested(index);
/*auto a_data = data.value<AssignableItemData>();
auto proxy = proxyData.value<AssignableProxy*>();
match(a_data.assignableInfo()) (
pattern(as<RangeInfo>(arg)) = [this, &menu, proxy, &editConn](auto ri) {
pattern(as<RangeInfo>(arg)) = [this, &menu, proxy, &editConn](auto
ri) {
//functionEditorRequested(*proxy, ri);
connect(&editConn, &QAction::triggered, [this, proxy, ri] {
functionEditorRequested(*proxy, ri);
@@ -44,10 +44,9 @@ DeviceTreeView::DeviceTreeView(QWidget *parent)
},
pattern(_) = []{}
);*/
}
});
m_delegate = new DeviceModelDelegate(this);
setItemDelegate(m_delegate);
}

View File

@@ -15,25 +15,24 @@ class DeviceTreeView : public QTreeView {
public:
DeviceTreeView(QWidget *parent = nullptr);
// Accessor method for connecting everything in the browser
//const DeviceModel &deviceModel() {return m_deviceModel;}
// const DeviceModel &deviceModel() {return m_deviceModel;}
// TODO: make this more generalized
// Defers the complexity to DeviceBrowser
// TODO: this can be handled in the delegate with QAbstractItemDelegate::editorEvent
boost::signals2::signal<void(QModelIndex&)> functionEditorRequested;
boost::signals2::signal<void(QModelIndex &)> functionEditorRequested;
protected:
/* Workaround for the retarded behavior of waiting for a double click,
you can't even disable it! */
bool edit(const QModelIndex &index, QAbstractItemView::EditTrigger trigger,
QEvent *event) {
bool edit(const QModelIndex &index, QAbstractItemView::EditTrigger trigger, QEvent *event) {
return QTreeView::edit(index,
trigger == QAbstractItemView::SelectedClicked ?
QAbstractItemView::AllEditTriggers : trigger, event);
trigger == QAbstractItemView::SelectedClicked
? QAbstractItemView::AllEditTriggers
: trigger,
event);
}
// TODO: allow to start editing with the keyboard
EditTriggers editTriggers() {
return QAbstractItemView::AllEditTriggers;
}
EditTriggers editTriggers() { return QAbstractItemView::AllEditTriggers; }
private:
//DeviceModel &m_deviceModel;
// DeviceModel &m_deviceModel;
DeviceModelDelegate *m_delegate;
};

View File

@@ -12,37 +12,31 @@ namespace TC = TuxClocker;
class DoubleRangeEditor : public AbstractAssignableEditor {
public:
DoubleRangeEditor(QWidget *parent = nullptr)
: AbstractAssignableEditor(parent) {
DoubleRangeEditor(QWidget *parent = nullptr) : AbstractAssignableEditor(parent) {
setAutoFillBackground(true);
m_layout = new QHBoxLayout(this);
m_spinBox = new QDoubleSpinBox;
m_slider = new QSlider(Qt::Horizontal);
connect(m_slider, &QSlider::rangeChanged, [this](int min, int max) {
m_spinBox->setRange(toDouble(min), toDouble(max));
});
connect(m_spinBox, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
[this](double val) {
m_slider->setValue(toInt(val));
});
connect(m_slider, &QSlider::valueChanged, [this](int val) {
m_spinBox->setValue(toDouble(val));
});
[this](double val) { m_slider->setValue(toInt(val)); });
connect(m_slider, &QSlider::valueChanged,
[this](int val) { m_spinBox->setValue(toDouble(val)); });
m_layout->setMargin(0);
m_layout->addWidget(m_slider);
m_layout->addWidget(m_spinBox);
setLayout(m_layout);
}
DoubleRangeEditor(TC::Device::Range<double> range, QWidget *parent = nullptr)
: DoubleRangeEditor(parent) {
: DoubleRangeEditor(parent) {
setRange(range);
}
virtual QVariant assignableData() override {return m_spinBox->value();}
virtual QString displayData() override {
return QString::number(m_spinBox->value());
}
virtual QVariant assignableData() override { return m_spinBox->value(); }
virtual QString displayData() override { return QString::number(m_spinBox->value()); }
virtual void setAssignableData(QVariant data) override {
m_spinBox->setValue(data.toDouble());
}
@@ -51,14 +45,10 @@ public:
}
private:
// How many digits are displayed after the dot
constexpr int doubleDecimals() {return 2;}
double toDouble(int i) {
return static_cast<double>(i) / pow(10, doubleDecimals());
}
int toInt(double d) {
return static_cast<int>(d * pow(10, doubleDecimals()));
}
constexpr int doubleDecimals() { return 2; }
double toDouble(int i) { return static_cast<double>(i) / pow(10, doubleDecimals()); }
int toInt(double d) { return static_cast<int>(d * pow(10, doubleDecimals())); }
QDoubleSpinBox *m_spinBox;
QHBoxLayout *m_layout;
QSlider *m_slider;

View File

@@ -7,174 +7,167 @@
#include <QScreen>
#include <QWindow>
DragChartView::DragChartView(QWidget *parent) : QChartView(parent)
{
chart()->installEventFilter(this);
setRenderHint(QPainter::Antialiasing);
DragChartView::DragChartView(QWidget *parent) : QChartView(parent) {
chart()->installEventFilter(this);
m_toolTipLabel = new QLabel;
m_toolTipLabel->setWindowFlag(Qt::ToolTip);
setRenderHint(QPainter::Antialiasing);
m_dragCanStart = false;
m_mouseInLimitArea = false;
m_scatterPressed = false;
m_toolTipLabel = new QLabel;
m_toolTipLabel->setWindowFlag(Qt::ToolTip);
m_leftLineFillerItem = new QGraphicsLineItem;
m_rightLineFillerItem = new QGraphicsLineItem;
m_dragCanStart = false;
m_mouseInLimitArea = false;
m_scatterPressed = false;
m_leftLineFillerItem = new QGraphicsLineItem;
m_rightLineFillerItem = new QGraphicsLineItem;
m_leftLineFillerItem->setPen(fillerLinePen());
m_rightLineFillerItem->setPen(fillerLinePen());
chart()->scene()->addItem(m_leftLineFillerItem);
chart()->scene()->addItem(m_rightLineFillerItem);
chart()->scene()->addItem(m_leftLineFillerItem);
chart()->scene()->addItem(m_rightLineFillerItem);
chart()->legend()->setVisible(false);
m_chartMargin = m_series.markerSize() / 2;
// Resize axes by margin
connect(&m_xAxis, &QValueAxis::rangeChanged, [=](qreal min, qreal max) {
m_limitRect.setLeft(min);
m_limitRect.setRight(max);
m_limitRect.setTopLeft(QPointF(min, m_limitRect.top()));
m_limitRect.setBottomRight(QPointF(max, m_limitRect.bottom()));
if (chart()->rect().isNull()) {
return;
}
// Convert m_chartMargin to chart value
auto valueDelta = abs(chart()->mapToValue(QPointF(0, 0)).x() - chart()->mapToValue(QPointF(m_chartMargin, 0)).x());
m_xAxis.blockSignals(true); // Don't go to an infinite loop
m_xAxis.setRange(min - valueDelta, max + valueDelta);
m_xAxis.blockSignals(false);
});
connect(&m_yAxis, &QValueAxis::rangeChanged, [=](qreal min, qreal max) {
// Update limit rect
m_limitRect.setBottom(min);
m_limitRect.setTop(max);
m_limitRect.setTopLeft(QPointF(m_limitRect.left(), max));
m_limitRect.setBottomRight(QPointF(m_limitRect.right(), min));
if (chart()->rect().isNull()) {
return;
}
auto valueDelta = abs(chart()->mapToValue(QPointF(0, 0)).x() - chart()->mapToValue(QPointF(m_chartMargin, 0)).x());
m_yAxis.blockSignals(true); // Don't go to an infinite loop
m_yAxis.setRange(min - valueDelta, max + valueDelta);
m_yAxis.blockSignals(false);
});
// Delete filler items when points are removed
connect(&m_series, &QScatterSeries::pointRemoved, [=]() {
chart()->scene()->removeItem(m_lineFillerItems.last());
delete m_lineFillerItems.last();
m_lineFillerItems.pop_back();
m_chartMargin = m_series.markerSize() / 2;
// Resize axes by margin
connect(&m_xAxis, &QValueAxis::rangeChanged, [=](qreal min, qreal max) {
m_limitRect.setLeft(min);
m_limitRect.setRight(max);
m_limitRect.setTopLeft(QPointF(min, m_limitRect.top()));
m_limitRect.setBottomRight(QPointF(max, m_limitRect.bottom()));
if (chart()->rect().isNull()) {
return;
}
// Convert m_chartMargin to chart value
auto valueDelta = abs(chart()->mapToValue(QPointF(0, 0)).x() -
chart()->mapToValue(QPointF(m_chartMargin, 0)).x());
m_xAxis.blockSignals(true); // Don't go to an infinite loop
m_xAxis.setRange(min - valueDelta, max + valueDelta);
m_xAxis.blockSignals(false);
});
connect(&m_yAxis, &QValueAxis::rangeChanged, [=](qreal min, qreal max) {
// Update limit rect
m_limitRect.setBottom(min);
m_limitRect.setTop(max);
m_limitRect.setTopLeft(QPointF(m_limitRect.left(), max));
m_limitRect.setBottomRight(QPointF(m_limitRect.right(), min));
if (chart()->rect().isNull()) {
return;
}
auto valueDelta = abs(chart()->mapToValue(QPointF(0, 0)).x() -
chart()->mapToValue(QPointF(m_chartMargin, 0)).x());
m_yAxis.blockSignals(true); // Don't go to an infinite loop
m_yAxis.setRange(min - valueDelta, max + valueDelta);
m_yAxis.blockSignals(false);
});
// Delete filler items when points are removed
connect(&m_series, &QScatterSeries::pointRemoved, [=]() {
chart()->scene()->removeItem(m_lineFillerItems.last());
delete m_lineFillerItems.last();
m_lineFillerItems.pop_back();
drawFillerLines(&m_series);
});
// Add filler item when point is added
connect(&m_series, &QScatterSeries::pointAdded, [=]() {
});
// Add filler item when point is added
connect(&m_series, &QScatterSeries::pointAdded, [=]() {
if (m_series.pointsVector().length() < 2)
return;
auto item = new QGraphicsLineItem;
item->setPen(fillerLinePen());
m_lineFillerItems.append(item);
chart()->scene()->addItem(item);
auto item = new QGraphicsLineItem;
item->setPen(fillerLinePen());
m_lineFillerItems.append(item);
chart()->scene()->addItem(item);
drawFillerLines(&m_series);
});
connect(&m_series, &QScatterSeries::pointsReplaced, [=]() {
// Delete filler items
for (auto item : m_lineFillerItems) {
delete item;
}
m_lineFillerItems.clear();
// Create new ones
for (int i = 0; i < m_series.pointsVector().length(); i++) {
auto item = new QGraphicsLineItem;
item->setPen(fillerLinePen());
m_lineFillerItems.append(item);
chart()->scene()->addItem(item);
}
drawFillerLines(&m_series);
});
connect(&m_series, &QScatterSeries::pressed, [=](QPointF point) {
m_dragCanStart = true;
m_latestScatterPoint = point;
m_scatterPressed = true;
});
connect(&m_series, &QScatterSeries::clicked, [=](const QPointF point) {
m_scatterPressed = false;
if (m_dragActive) {
m_dragActive = false;
emit dragEnded(point);
}
else {
// Just a click, delete point
m_series.remove(point);
}
});
});
chart()->addSeries(&m_series);
chart()->addAxis(&m_xAxis, Qt::AlignBottom);
chart()->addAxis(&m_yAxis, Qt::AlignLeft);
m_series.attachAxis(&m_xAxis);
m_series.attachAxis(&m_yAxis);
m_xAxis.setRange(0, 25);
m_yAxis.setRange(0, 25);
connect(&m_series, &QScatterSeries::pointsReplaced, [=]() {
// Delete filler items
for (auto item : m_lineFillerItems) {
delete item;
}
m_lineFillerItems.clear();
// Create new ones
for (int i = 0; i < m_series.pointsVector().length(); i++) {
auto item = new QGraphicsLineItem;
item->setPen(fillerLinePen());
m_lineFillerItems.append(item);
chart()->scene()->addItem(item);
}
drawFillerLines(&m_series);
});
chart()->setBackgroundRoundness(0);
// Set theme colors
chart()->setBackgroundBrush(QBrush(QPalette().color(QPalette::Background)));
chart()->legend()->setLabelColor(QPalette().color(QPalette::Text));
m_yAxis.setLabelsColor(QPalette().color(QPalette::Text));
m_xAxis.setLabelsColor(QPalette().color(QPalette::Text));
m_yAxis.setTitleBrush(QBrush(QPalette().color(QPalette::Text)));
m_xAxis.setTitleBrush(QBrush(QPalette().color(QPalette::Text)));
// Set cursor to indicate dragging
connect(this, &DragChartView::dragStarted, [=]() {
setCursor(Qt::ClosedHandCursor);
});
connect(this, &DragChartView::dragEnded, [=]() {
if (m_mouseInLimitArea) {
setCursor(Qt::CrossCursor);
}
else {
setCursor(Qt::ArrowCursor);
}
});
connect(this, &DragChartView::limitAreaEntered, [=]() {
setCursor(Qt::CrossCursor);
});
connect(this, &DragChartView::limitAreaExited, [=]() {
if (cursor().shape() != Qt::ClosedHandCursor) {
setCursor(Qt::ArrowCursor);
}
});
connect(&m_series, &QScatterSeries::pressed, [=](QPointF point) {
m_dragCanStart = true;
m_latestScatterPoint = point;
m_scatterPressed = true;
});
connect(&m_series, &QScatterSeries::clicked, [=](const QPointF point) {
m_scatterPressed = false;
if (m_dragActive) {
m_dragActive = false;
emit dragEnded(point);
} else {
// Just a click, delete point
m_series.remove(point);
}
});
chart()->addSeries(&m_series);
chart()->addAxis(&m_xAxis, Qt::AlignBottom);
chart()->addAxis(&m_yAxis, Qt::AlignLeft);
m_series.attachAxis(&m_xAxis);
m_series.attachAxis(&m_yAxis);
m_xAxis.setRange(0, 25);
m_yAxis.setRange(0, 25);
chart()->setBackgroundRoundness(0);
// Set theme colors
chart()->setBackgroundBrush(QBrush(QPalette().color(QPalette::Background)));
chart()->legend()->setLabelColor(QPalette().color(QPalette::Text));
m_yAxis.setLabelsColor(QPalette().color(QPalette::Text));
m_xAxis.setLabelsColor(QPalette().color(QPalette::Text));
m_yAxis.setTitleBrush(QBrush(QPalette().color(QPalette::Text)));
m_xAxis.setTitleBrush(QBrush(QPalette().color(QPalette::Text)));
// Set cursor to indicate dragging
connect(this, &DragChartView::dragStarted, [=]() { setCursor(Qt::ClosedHandCursor); });
connect(this, &DragChartView::dragEnded, [=]() {
if (m_mouseInLimitArea) {
setCursor(Qt::CrossCursor);
} else {
setCursor(Qt::ArrowCursor);
}
});
connect(this, &DragChartView::limitAreaEntered, [=]() { setCursor(Qt::CrossCursor); });
connect(this, &DragChartView::limitAreaExited, [=]() {
if (cursor().shape() != Qt::ClosedHandCursor) {
setCursor(Qt::ArrowCursor);
}
});
}
void DragChartView::setVector(const QVector <QPointF> vector) {
m_series.replace(vector);
}
void DragChartView::setVector(const QVector<QPointF> vector) { m_series.replace(vector); }
void DragChartView::setRange(qreal xmin, qreal xmax, qreal ymin, qreal ymax) {
m_series.clear();
@@ -183,192 +176,195 @@ void DragChartView::setRange(qreal xmin, qreal xmax, qreal ymin, qreal ymax) {
}
bool DragChartView::event(QEvent *event) {
//qDebug() << event->type();
// qDebug() << event->type();
if (event->type() == QEvent::Resize || event->type() == QEvent::UpdateLater) {
// Chart has a geometry when this is true
drawFillerLines(&m_series);
}
if (event->type() == QEvent::Leave && !m_dragActive) {
// Set to normal cursor
setCursor(Qt::ArrowCursor);
}
if (event->type() == QEvent::Resize || event->type() == QEvent::UpdateLater) {
// Chart has a geometry when this is true
drawFillerLines(&m_series);
}
return QChartView::event(event);
if (event->type() == QEvent::Leave && !m_dragActive) {
// Set to normal cursor
setCursor(Qt::ArrowCursor);
}
return QChartView::event(event);
}
void DragChartView::mousePressEvent(QMouseEvent *event) {
if (event->button() == Qt::LeftButton) {
m_dragStartPosition = event->pos();
}
if (event->button() == Qt::LeftButton) {
m_dragStartPosition = event->pos();
}
QChartView::mousePressEvent(event);
QChartView::mousePressEvent(event);
}
void DragChartView::mouseMoveEvent(QMouseEvent *event) {
if (m_limitRect.contains(chart()->mapToValue(event->pos())) && !m_mouseInLimitArea) {
m_mouseInLimitArea = true;
emit limitAreaEntered();
}
if (!m_limitRect.contains(chart()->mapToValue(event->pos())) && m_mouseInLimitArea) {
m_mouseInLimitArea = false;
emit limitAreaExited();
}
if (!(event->buttons() & Qt::LeftButton)) {
return;
}
if ((event->pos() - m_dragStartPosition).manhattanLength() < QApplication::startDragDistance() || !m_dragCanStart) {
return;
}
void DragChartView::mouseMoveEvent(QMouseEvent *event) {
if (m_limitRect.contains(chart()->mapToValue(event->pos())) && !m_mouseInLimitArea) {
m_mouseInLimitArea = true;
emit limitAreaEntered();
}
// Start drag
emit dragStarted(m_dragStartPosition);
m_dragActive = true;
if (!m_limitRect.contains(chart()->mapToValue(event->pos())) && m_mouseInLimitArea) {
m_mouseInLimitArea = false;
emit limitAreaExited();
}
if (m_toolTipLabel->isHidden()) {
m_toolTipLabel->show();
}
if (!(event->buttons() & Qt::LeftButton)) {
return;
}
if ((event->pos() - m_dragStartPosition).manhattanLength() <
QApplication::startDragDistance() ||
!m_dragCanStart) {
return;
}
m_toolTipLabel->setText(QString("%1, %2")
.arg(QString::number(m_latestScatterPoint.x()),
QString::number(m_latestScatterPoint.y())));
// FIXME : doesn't work properly when screen is switched(?)
m_toolTipLabel->move(event->screenPos().toPoint() + toolTipOffset(this, event->windowPos().toPoint()));
// Don't move point out of bounds
if (m_limitRect.contains(chart()->mapToValue(event->pos()))) {
replaceMovedPoint(m_latestScatterPoint, chart()->mapToValue(event->pos(), &m_series));
}
else {
QPointF point(chart()->mapToValue(event->pos()));
// Set the point value to the constraint where it exceeds it
if (chart()->mapToValue(event->pos()).x() > m_limitRect.right()) {
point.setX(m_limitRect.right());
}
if (chart()->mapToValue(event->pos()).x() < m_limitRect.left()) {
point.setX(m_limitRect.left());
}
if (chart()->mapToValue(event->pos()).y() > m_limitRect.top()) {
point.setY(m_limitRect.top());
}
if (chart()->mapToValue(event->pos()).y() < m_limitRect.bottom()) {
point.setY(m_limitRect.bottom());
}
replaceMovedPoint(m_latestScatterPoint, point);
}
// Start drag
emit dragStarted(m_dragStartPosition);
drawFillerLines(&m_series);
m_dragActive = true;
if (m_toolTipLabel->isHidden()) {
m_toolTipLabel->show();
}
m_toolTipLabel->setText(QString("%1, %2").arg(
QString::number(m_latestScatterPoint.x()), QString::number(m_latestScatterPoint.y())));
// FIXME : doesn't work properly when screen is switched(?)
m_toolTipLabel->move(
event->screenPos().toPoint() + toolTipOffset(this, event->windowPos().toPoint()));
// Don't move point out of bounds
if (m_limitRect.contains(chart()->mapToValue(event->pos()))) {
replaceMovedPoint(
m_latestScatterPoint, chart()->mapToValue(event->pos(), &m_series));
} else {
QPointF point(chart()->mapToValue(event->pos()));
// Set the point value to the constraint where it exceeds it
if (chart()->mapToValue(event->pos()).x() > m_limitRect.right()) {
point.setX(m_limitRect.right());
}
if (chart()->mapToValue(event->pos()).x() < m_limitRect.left()) {
point.setX(m_limitRect.left());
}
if (chart()->mapToValue(event->pos()).y() > m_limitRect.top()) {
point.setY(m_limitRect.top());
}
if (chart()->mapToValue(event->pos()).y() < m_limitRect.bottom()) {
point.setY(m_limitRect.bottom());
}
replaceMovedPoint(m_latestScatterPoint, point);
}
drawFillerLines(&m_series);
}
void DragChartView::mouseReleaseEvent(QMouseEvent *event) {
m_dragCanStart = false;
m_dragCanStart = false;
if (!m_scatterPressed &&
m_limitRect.contains(chart()->mapToValue(event->pos()))) {
if (!m_scatterPressed && m_limitRect.contains(chart()->mapToValue(event->pos()))) {
// Add a new point to series
m_series.append(chart()->mapToValue(event->pos()));
drawFillerLines(&m_series);
}
m_toolTipLabel->hide();
QChartView::mouseReleaseEvent(event);
}
m_toolTipLabel->hide();
QChartView::mouseReleaseEvent(event);
}
void DragChartView::wheelEvent(QWheelEvent *event) {
qreal factor = event->angleDelta().y() > 0 ? 0.5 : 2.0;
qreal factor = event->angleDelta().y() > 0 ? 0.5 : 2.0;
//chart()->zoom(factor);
event->accept();
// chart()->zoom(factor);
event->accept();
drawFillerLines(&m_series);
drawFillerLines(&m_series);
QChartView::wheelEvent(event);
QChartView::wheelEvent(event);
}
void DragChartView::replaceMovedPoint(const QPointF old, const QPointF new_) {
m_series.replace(old, new_);
m_series.replace(old, new_);
m_latestScatterPoint = new_;
m_latestScatterPoint = new_;
}
QVector <QPointF> DragChartView::sortPointFByAscendingX(const QVector<QPointF> points) {
QVector <qreal> ascendingX;
QVector <qreal> originalY;
QVector<QPointF> DragChartView::sortPointFByAscendingX(const QVector<QPointF> points) {
QVector<qreal> ascendingX;
QVector<qreal> originalY;
for (QPointF point : points) {
ascendingX.append(point.x());
originalY.append(point.y());
}
for (QPointF point : points) {
ascendingX.append(point.x());
originalY.append(point.y());
}
std::sort(ascendingX.begin(), ascendingX.end());
std::sort(ascendingX.begin(), ascendingX.end());
QVector <QPointF> sorted;
QVector<QPointF> sorted;
// Find the original y values for x values
for (qreal x : ascendingX) {
for (QPointF point : points) {
if (qFuzzyCompare(x, point.x())) {
sorted.append(QPointF(x, point.y()));
break;
}
}
}
// Find the original y values for x values
for (qreal x : ascendingX) {
for (QPointF point : points) {
if (qFuzzyCompare(x, point.x())) {
sorted.append(QPointF(x, point.y()));
break;
}
}
}
return sorted;
return sorted;
}
void DragChartView::drawFillerLines(QScatterSeries *series) {
// TODO: line isn't drawn between points whose x is the same
// TODO: line isn't drawn between points whose x is the same
// Sort points by ascending x
QVector <QPointF> sorted = sortPointFByAscendingX(series->pointsVector());
QVector<QPointF> sorted = sortPointFByAscendingX(series->pointsVector());
if (sorted.isEmpty()) {
return;
}
if (sorted.isEmpty()) {
return;
}
for (int i = 0; i < sorted.length() - 1; i++) {
m_lineFillerItems[i]->setLine(QLineF(chart()->mapToPosition(sorted[i]),
chart()->mapToPosition(sorted[i + 1])));
}
for (int i = 0; i < sorted.length() - 1; i++) {
m_lineFillerItems[i]->setLine(QLineF(
chart()->mapToPosition(sorted[i]), chart()->mapToPosition(sorted[i + 1])));
}
m_leftLineFillerItem->setLine(
QLineF(chart()->mapToPosition(QPointF(
m_xAxis.min(), sorted[0].y())),
m_leftLineFillerItem->setLine(
QLineF(chart()->mapToPosition(QPointF(m_xAxis.min(), sorted[0].y())),
chart()->mapToPosition(sorted[0])));
m_rightLineFillerItem->setLine(
QLineF(chart()->mapToPosition(sorted.last()),
chart()->mapToPosition(QPointF(m_xAxis.max(), sorted.last().y()))));
m_rightLineFillerItem->setLine(QLineF(chart()->mapToPosition(sorted.last()),
chart()->mapToPosition(QPointF(m_xAxis.max(), sorted.last().y()))));
chart()->update();
chart()->update();
}
bool DragChartView::eventFilter(QObject *obj, QEvent *event) {
static bool resized = false;
if (obj == chart() && event->type() == QEvent::WindowActivate && !resized) {
resized = true;
emit m_xAxis.rangeChanged(m_xAxis.min(), m_xAxis.max());
emit m_yAxis.rangeChanged(m_yAxis.min(), m_yAxis.max());
}
return QObject::eventFilter(obj, event);
static bool resized = false;
if (obj == chart() && event->type() == QEvent::WindowActivate && !resized) {
resized = true;
emit m_xAxis.rangeChanged(m_xAxis.min(), m_xAxis.max());
emit m_yAxis.rangeChanged(m_yAxis.min(), m_yAxis.max());
}
return QObject::eventFilter(obj, event);
}
QPoint DragChartView::toolTipOffset(QWidget *widget, const QPoint windowCursorPos) {
QRect screenRect = widget->window()->windowHandle()->screen()->geometry();
if (screenRect.width() > screenRect.height()) {
// Use x offset for screens that are wider than high
// Set the offset to the side that has more space
int xOffset = (windowCursorPos.x() > widget->window()->rect().width() / 2) ? -qRound(toolTipMargin() * screenRect.width()) : qRound(toolTipMargin() * screenRect.width());
return QPoint(xOffset, 0);
}
int yOffset = (windowCursorPos.y() > widget->window()->rect().height() / 2) ? -qRound(toolTipMargin() * screenRect.height()) :qRound(toolTipMargin() * screenRect.height());
return QPoint(0, yOffset);
QRect screenRect = widget->window()->windowHandle()->screen()->geometry();
if (screenRect.width() > screenRect.height()) {
// Use x offset for screens that are wider than high
// Set the offset to the side that has more space
int xOffset = (windowCursorPos.x() > widget->window()->rect().width() / 2)
? -qRound(toolTipMargin() * screenRect.width())
: qRound(toolTipMargin() * screenRect.width());
return QPoint(xOffset, 0);
}
int yOffset = (windowCursorPos.y() > widget->window()->rect().height() / 2)
? -qRound(toolTipMargin() * screenRect.height())
: qRound(toolTipMargin() * screenRect.height());
return QPoint(0, yOffset);
}

View File

@@ -9,58 +9,58 @@ using namespace QtCharts;
class DragChartView : public QChartView {
public:
DragChartView(QWidget *parent = nullptr);
QValueAxis &xAxis() {return m_xAxis;}
QValueAxis &yAxis() {return m_yAxis;}
void setVector(const QVector <QPointF> vector);
QVector <QPointF> vector() {return m_series.pointsVector();}
// Clear the points and set new range
void setRange(qreal xmin, qreal xmax, qreal ymin, qreal ymax);
DragChartView(QWidget *parent = nullptr);
QValueAxis &xAxis() { return m_xAxis; }
QValueAxis &yAxis() { return m_yAxis; }
void setVector(const QVector<QPointF> vector);
QVector<QPointF> vector() { return m_series.pointsVector(); }
// Clear the points and set new range
void setRange(qreal xmin, qreal xmax, qreal ymin, qreal ymax);
protected:
bool event(QEvent*);
void mousePressEvent(QMouseEvent*);
void mouseMoveEvent(QMouseEvent*);
void mouseReleaseEvent(QMouseEvent*);
void wheelEvent(QWheelEvent*);
bool eventFilter(QObject *obj, QEvent *event); // Get notified when chart has geometry
bool event(QEvent *);
void mousePressEvent(QMouseEvent *);
void mouseMoveEvent(QMouseEvent *);
void mouseReleaseEvent(QMouseEvent *);
void wheelEvent(QWheelEvent *);
bool eventFilter(QObject *obj, QEvent *event); // Get notified when chart has geometry
signals:
void dragStarted(const QPointF point);
void dragEnded(const QPointF point);
void limitAreaEntered();
void limitAreaExited();
void dragStarted(const QPointF point);
void dragEnded(const QPointF point);
void limitAreaEntered();
void limitAreaExited();
private:
Q_OBJECT
static QPoint toolTipOffset(QWidget *widget, const QPoint windowCursorPos); // Get tooltip offset based on current screen
static double toolTipMargin() {return 0.02;}
QColor fillerLineColor() {
return QPalette().color(QPalette::Highlight);
}
QPen fillerLinePen() {
return QPen(fillerLineColor(), 3);
}
qreal m_chartMargin; // Margin in pixels between a scatter point and axis edge. Add this value on either edge of the axes in order for chatter points to be unable to go partially out of the viewport.
QRectF m_limitRect; // Scatter points can't be moved outside this rect
bool m_mouseInLimitArea;
bool m_scatterPressed; // For detecting if mouse release should delete or add a new point
QVector <QPointF> sortPointFByAscendingX(const QVector <QPointF> points);
void drawFillerLines(QScatterSeries *series);
QLabel *m_toolTipLabel;
QScatterSeries m_series;
QPoint m_dragStartPosition;
bool m_dragCanStart; // Was a point clicked and not released before drag should start
bool m_dragActive;
QPointF m_latestScatterPoint;
void replaceMovedPoint(const QPointF old, const QPointF new_);
QVector <QGraphicsLineItem*> m_lineFillerItems; // Filler lines between points whose amount is n - 1 for nonzero n
QGraphicsLineItem *m_leftLineFillerItem;
QGraphicsLineItem *m_rightLineFillerItem;
QValueAxis m_xAxis, m_yAxis;
Q_OBJECT
static QPoint toolTipOffset(QWidget *widget,
const QPoint windowCursorPos); // Get tooltip offset based on current screen
static double toolTipMargin() { return 0.02; }
QColor fillerLineColor() { return QPalette().color(QPalette::Highlight); }
QPen fillerLinePen() { return QPen(fillerLineColor(), 3); }
qreal m_chartMargin; // Margin in pixels between a scatter point and axis edge. Add this
// value on either edge of the axes in order for chatter points to be
// unable to go partially out of the viewport.
QRectF m_limitRect; // Scatter points can't be moved outside this rect
bool m_mouseInLimitArea;
bool m_scatterPressed; // For detecting if mouse release should delete or add a new point
QVector<QPointF> sortPointFByAscendingX(const QVector<QPointF> points);
void drawFillerLines(QScatterSeries *series);
QLabel *m_toolTipLabel;
QScatterSeries m_series;
QPoint m_dragStartPosition;
bool m_dragCanStart; // Was a point clicked and not released before drag should start
bool m_dragActive;
QPointF m_latestScatterPoint;
void replaceMovedPoint(const QPointF old, const QPointF new_);
QVector<QGraphicsLineItem *>
m_lineFillerItems; // Filler lines between points whose amount is n - 1 for nonzero n
QGraphicsLineItem *m_leftLineFillerItem;
QGraphicsLineItem *m_rightLineFillerItem;
QValueAxis m_xAxis, m_yAxis;
};

View File

@@ -15,36 +15,33 @@ EnumEditor::EnumEditor(QWidget *parent) : AbstractAssignableEditor(parent) {
setAutoFillBackground(true);
auto layout = new QHBoxLayout(this);
layout->setMargin(0);
m_comboBox = new QComboBox;
layout->addWidget(m_comboBox);
}
EnumEditor::EnumEditor(TuxClocker::Device::EnumerationVec enums, QWidget *parent)
: EnumEditor(parent) {
auto qStrings = QVector<QString>::fromStdVector(transform([](auto e) {
return QString::fromStdString(e.name);
}, enums));
: EnumEditor(parent) {
auto qStrings = QVector<QString>::fromStdVector(
transform([](auto e) { return QString::fromStdString(e.name); }, enums));
for (uint i = 0; i < qStrings.length(); i++) {
auto item = new QStandardItem;
item->setText(qStrings[i]);
item->setData(i, KeyRole);
m_model.appendRow(item);
}
m_model.sort(0);
m_comboBox->setModel(&m_model);
}
QVariant EnumEditor::assignableData() {
auto r = m_model.index(m_comboBox->currentIndex(), 0)
.data(KeyRole).toUInt();
auto r = m_model.index(m_comboBox->currentIndex(), 0).data(KeyRole).toUInt();
return r;
}
QString EnumEditor::displayData() {
auto r = m_model.index(m_comboBox->currentIndex(), 0)
.data(Qt::DisplayRole).toString();
auto r = m_model.index(m_comboBox->currentIndex(), 0).data(Qt::DisplayRole).toString();
return r;
}
void EnumEditor::setAssignableData(QVariant data) {

View File

@@ -1,2 +1 @@
#include "FlagEditor.hpp"

View File

@@ -16,26 +16,25 @@
#include <QStandardItemModel>
#include <QStylePainter>
template <typename T>
class FlagEditor : public QComboBox {
template <typename T> class FlagEditor : public QComboBox {
public:
// Need to do the templated stuff in the header because C++ is retarded
FlagEditor(QVector<std::tuple<QString, QIcon, T>> flagData,
QWidget *parent = nullptr) : QComboBox(parent), m_skipNextHide(false) {
FlagEditor(QVector<std::tuple<QString, QIcon, T>> flagData, QWidget *parent = nullptr)
: QComboBox(parent), m_skipNextHide(false) {
view()->viewport()->installEventFilter(this);
auto model = new QStandardItemModel(this);
for (const auto &flagItem : flagData) {
auto item = new QStandardItem;
auto [text, icon, flag] = flagItem;
//auto icon = style()->standardIcon(QStyle::SP_ComputerIcon);
//item->setData(icon, Qt::DecorationRole);
// auto icon = style()->standardIcon(QStyle::SP_ComputerIcon);
// item->setData(icon, Qt::DecorationRole);
item->setCheckable(true);
item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
item->setData(Qt::Checked, Qt::CheckStateRole);
item->setData(icon ,Qt::DecorationRole);
item->setData(icon, Qt::DecorationRole);
item->setText(text);
// Cast to uint so we don't need to register the type
auto uFlag = static_cast<uint>(flag);
@@ -44,12 +43,10 @@ public:
model->appendRow(item);
}
setModel(model);
connect(model, &QStandardItemModel::itemChanged,
[this](QStandardItem *item) {
connect(model, &QStandardItemModel::itemChanged, [this](QStandardItem *item) {
auto flag = static_cast<QFlags<T>>(item->data(FlagRole).toUInt());
auto checkState = item->data(Qt::CheckStateRole)
.value<Qt::CheckState>();
auto checkState = item->data(Qt::CheckStateRole).value<Qt::CheckState>();
// Shouldn't ever be partially checked
if (checkState == Qt::Unchecked)
// Remove flag
@@ -58,24 +55,24 @@ public:
m_flags |= flag;
flagsChanged(m_flags);
});
// Cause a repaint on changed flags
// Boost won't let me discard the argument :(
flagsChanged.connect([this](auto) {update();});
flagsChanged.connect([this](auto) { update(); });
}
void setFlags(QFlags<T> flags) {
m_flags = flags;
flagsChanged(flags);
auto handledFlags = m_flagHash.keys();
auto uFlags = static_cast<uint>(flags);
for (const auto &flag : handledFlags) {
if (uFlags & flag)
m_flagHash.value(flag)->setData(Qt::Checked, Qt::CheckStateRole);
}
}
//QFlags<T> flags() {}
// QFlags<T> flags() {}
bool eventFilter(QObject *obj, QEvent *ev) {
if (ev->type() == QEvent::MouseButtonPress && obj == view()->viewport())
m_skipNextHide = true;
@@ -89,7 +86,7 @@ public:
}
boost::signals2::signal<void(QFlags<T>)> flagsChanged;
protected:
void paintEvent(QPaintEvent*) {
void paintEvent(QPaintEvent *) {
// Override paintEvent to draw custom text in the box
QStylePainter painter(this);
QStyleOptionComboBox opt;
@@ -97,14 +94,16 @@ protected:
QPalette rp;
rp.setColor(QPalette::Highlight, Qt::red);
opt.palette = rp;
// Show which flags are selected
auto items = m_flagHash.values();
// Not sure why using 'auto' breaks something mysteriously here
QList<QStandardItem*> checked = fplus::keep_if([](QStandardItem *item) {
return item->data(Qt::CheckStateRole)
.value<Qt::CheckState>() == Qt::Checked;
}, items);
QList<QStandardItem *> checked = fplus::keep_if(
[](QStandardItem *item) {
return item->data(Qt::CheckStateRole).value<Qt::CheckState>() ==
Qt::Checked;
},
items);
// Where the first icon is drawn
auto startY = opt.rect.top() + iconSize().height() / 2;
@@ -117,10 +116,9 @@ protected:
// Draw icons of selected items
QRect drawRect(QPoint(startX, startY), iconSize());
for (const auto &item : checked) {
auto pixmap = item->data(Qt::DecorationRole)
.value<QIcon>().pixmap(iconSize());
painter.drawItemPixmap(drawRect,
Qt::AlignCenter, pixmap);
auto pixmap =
item->data(Qt::DecorationRole).value<QIcon>().pixmap(iconSize());
painter.drawItemPixmap(drawRect, Qt::AlignCenter, pixmap);
drawRect.moveRight(drawRect.right() + deltaX);
}
}
@@ -128,5 +126,5 @@ private:
bool m_skipNextHide;
const int FlagRole = Qt::UserRole + 1;
QFlags<T> m_flags;
QHash<uint, QStandardItem*> m_flagHash;
QHash<uint, QStandardItem *> m_flagHash;
};

View File

@@ -32,21 +32,19 @@
// Delet this
namespace p = mpark::patterns;
Q_DECLARE_METATYPE(DynamicReadableProxy*)
Q_DECLARE_METATYPE(DynamicReadableProxy *)
// TODO: make constructor of the type data Editor a = Maybe (Range a)
class FunctionEditor : public QWidget {
public:
FunctionEditor(DeviceModel &model, TuxClocker::Device::RangeInfo rangeInfo,
AssignableProxy &proxy, QString nodeName,
QWidget *parent = nullptr)
: QWidget(parent), m_assignableProxy(proxy),
m_model(model), m_proxyModel(model),
m_rangeInfo(rangeInfo) {
AssignableProxy &proxy, QString nodeName, QWidget *parent = nullptr)
: QWidget(parent), m_assignableProxy(proxy), m_model(model), m_proxyModel(model),
m_rangeInfo(rangeInfo) {
m_proxyModel.setDisableFiltered(true);
m_proxyModel.setFlags(DeviceModel::DynamicReadable);
m_proxyModel.setShowIcons(false);
//m_proxyModel.setShowValueColumn(false);
// m_proxyModel.setShowValueColumn(false);
m_layout = new QGridLayout(this);
m_dependableReadableComboBox = new NodeSelector;
@@ -63,18 +61,13 @@ public:
m_layout->addWidget(m_dependableLabel, 0, 0, 1, 2);
m_layout->addWidget(m_dependableReadableComboBox, 1, 0, 1, 2);
m_dragView = new DragChartView;
p::match(rangeInfo) (
p::pattern(p::as<TuxClocker::Device::Range<double>>(p::arg))
= [this](auto dr) {
m_dragView->setRange(0, 100, dr.min, dr.max);
},
p::pattern(p::as<TuxClocker::Device::Range<int>>(p::arg))
= [this](auto ir) {
m_dragView->setRange(0, 100, ir.min, ir.max);
}
);
//m_dragView->setRange(0, 100, 0, 100);
p::match(rangeInfo)(
p::pattern(p::as<TuxClocker::Device::Range<double>>(p::arg)) =
[this](auto dr) { m_dragView->setRange(0, 100, dr.min, dr.max); },
p::pattern(p::as<TuxClocker::Device::Range<int>>(p::arg)) =
[this](auto ir) { m_dragView->setRange(0, 100, ir.min, ir.max); });
// m_dragView->setRange(0, 100, 0, 100);
m_layout->addWidget(m_dragView, 2, 0, 1, 2);
m_applyButton = new QPushButton("Apply");
// No connection to apply at first
@@ -82,25 +75,23 @@ public:
m_cancelButton = new QPushButton("Cancel");
m_layout->addWidget(m_cancelButton, 3, 0, 1, 1);
m_layout->addWidget(m_applyButton, 3, 1, 1, 1);
connect(m_applyButton, &QPushButton::clicked, [this] {
auto proxy = m_latestNodeIndex
.data(DeviceModel::DynamicReadableProxyRole)
.value<DynamicReadableProxy*>();
//qDebug() << proxy;
auto proxy = m_latestNodeIndex.data(DeviceModel::DynamicReadableProxyRole)
.value<DynamicReadableProxy *>();
// qDebug() << proxy;
auto points = m_dragView->vector();
if (points.length() < 2)
return;
//qDebug() << points;
auto conn = std::make_shared<DynamicReadableConnection<int>>(
*proxy, points);
// qDebug() << points;
auto conn =
std::make_shared<DynamicReadableConnection<int>>(*proxy, points);
assignableConnectionChanged(conn);
});
m_dragView->yAxis().setTitleText(nodeName);
m_dependableReadableComboBox->indexChanged
.connect([this](auto &index) {
m_dependableReadableComboBox->indexChanged.connect([this](auto &index) {
m_latestNodeIndex = index;
m_applyButton->setEnabled(true);
auto nodeName = index.data(Qt::DisplayRole).toString();
@@ -110,13 +101,13 @@ public:
setLayout(m_layout);
}
boost::signals2::signal<void(std::shared_ptr<AssignableConnection>)>
assignableConnectionChanged;
assignableConnectionChanged;
private:
AssignableProxy &m_assignableProxy;
DeviceModel &m_model;
DeviceProxyModel m_proxyModel;
DragChartView *m_dragView;
//NodeSelector *m_nodeSelector;
// NodeSelector *m_nodeSelector;
QComboBox *m_functionComboBox;
NodeSelector *m_dependableReadableComboBox;
QGridLayout *m_layout;

View File

@@ -14,37 +14,32 @@ class IntRangeEditor : public AbstractAssignableEditor {
public:
IntRangeEditor(QWidget *parent = nullptr) : AbstractAssignableEditor(parent) {
setAutoFillBackground(true);
m_layout = new QHBoxLayout(this);
m_layout->setMargin(0);
m_slider = new QSlider(Qt::Horizontal);
m_spinBox = new QSpinBox;
m_layout->addWidget(m_slider);
m_layout->addWidget(m_spinBox);
setLayout(m_layout);
connect(m_slider, &QSlider::rangeChanged, m_spinBox, &QSpinBox::setRange);
connect(m_slider, &QSlider::valueChanged, m_spinBox, &QSpinBox::setValue);
connect(m_spinBox, QOverload<int>::of(&QSpinBox::valueChanged), m_slider, &QSlider::setValue);
connect(m_spinBox, QOverload<int>::of(&QSpinBox::valueChanged), m_slider,
&QSlider::setValue);
}
IntRangeEditor(TC::Device::Range<int> range, QWidget *parent = nullptr)
: IntRangeEditor(parent) {
: IntRangeEditor(parent) {
setRange(range);
}
virtual QVariant assignableData() override {return m_slider->value();}
virtual QString displayData() override {
return QString::number(m_slider->value());
}
virtual void setAssignableData(QVariant data) override {
m_slider->setValue(data.toInt());
}
void setRange(TC::Device::Range<int> range) {
m_slider->setRange(range.min, range.max);
}
int value() {return m_slider->value();}
virtual QVariant assignableData() override { return m_slider->value(); }
virtual QString displayData() override { return QString::number(m_slider->value()); }
virtual void setAssignableData(QVariant data) override { m_slider->setValue(data.toInt()); }
void setRange(TC::Device::Range<int> range) { m_slider->setRange(range.min, range.max); }
int value() { return m_slider->value(); }
private:
QHBoxLayout *m_layout;
QSlider *m_slider;

View File

@@ -14,17 +14,16 @@
// TODO: If a disabled item is clicked, the item that is highlighted get selected instead
class NodeSelector : public QComboBox {
public:
NodeSelector(QWidget *parent = nullptr) : QComboBox(parent),
m_skipNextHide(false) {
NodeSelector(QWidget *parent = nullptr) : QComboBox(parent), m_skipNextHide(false) {
view()->viewport()->installEventFilter(this);
}
bool eventFilter(QObject *obj, QEvent *ev) {
if (ev->type() == QEvent::MouseButtonPress && obj == view()->viewport()) {
auto mouse_ev = static_cast<QMouseEvent*>(ev);
auto mouse_ev = static_cast<QMouseEvent *>(ev);
auto index = view()->indexAt(mouse_ev->pos());
// Hide popup when an enabled item is clicked
if (!view()->visualRect(index).contains(mouse_ev->pos()) ||
!(model()->flags(index) & Qt::ItemIsEnabled))
!(model()->flags(index) & Qt::ItemIsEnabled))
m_skipNextHide = true;
else if (model()->flags(index) & Qt::ItemIsSelectable) {
qDebug() << index.data(DeviceModel::DynamicReadableProxyRole);
@@ -45,7 +44,7 @@ public:
if (!view()->visualRect(index).contains(event->pos()) ||
!(model()->flags(index) & Qt::ItemIsEnabled))
qDebug() << index.data();
QComboBox::mouseReleaseEvent(event);
}*/
void setView(QAbstractItemView *view) {
@@ -57,12 +56,11 @@ public:
virtual void showPopup() override {
// TODO: don't account for scrollbar width when it isn't visible
// This is quite a weird way to do it but it works
auto vBarWidth =
QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent);
auto vBarWidth = QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent);
view()->setMinimumWidth(view()->sizeHintForColumn(0) + vBarWidth);
QComboBox::showPopup();
}
boost::signals2::signal<void(QModelIndex&)> indexChanged;
boost::signals2::signal<void(QModelIndex &)> indexChanged;
private:
bool m_skipNextHide;
};

View File

@@ -15,21 +15,16 @@ class AdaptorFactory {
public:
/* Returns a raw pointer since smart pointers caused some scope issues.
* TODO: try to use smart pointers instead? */
static std::optional<QDBusAbstractAdaptor*> adaptor(QObject *obj,
DeviceInterface iface) {
std::optional<QDBusAbstractAdaptor*> retval = std::nullopt;
match(iface) (
pattern(as<StaticReadable>(arg)) = [&](auto sr) {
retval = new StaticReadableAdaptor(obj, sr);
},
pattern(as<DynamicReadable>(arg)) = [&](auto dr) {
retval = new DynamicReadableAdaptor(obj, dr);
},
pattern(as<Assignable>(arg)) = [&](auto a) {
retval = new AssignableAdaptor(obj, a);
},
pattern(_) = []{}
);
static std::optional<QDBusAbstractAdaptor *> adaptor(QObject *obj, DeviceInterface iface) {
std::optional<QDBusAbstractAdaptor *> retval = std::nullopt;
match(iface)(
pattern(as<StaticReadable>(arg)) =
[&](auto sr) { retval = new StaticReadableAdaptor(obj, sr); },
pattern(as<DynamicReadable>(arg)) =
[&](auto dr) { retval = new DynamicReadableAdaptor(obj, dr); },
pattern(as<Assignable>(arg)) =
[&](auto a) { retval = new AssignableAdaptor(obj, a); },
pattern(_) = [] {});
return retval;
}
};

View File

@@ -25,41 +25,39 @@ Q_DECLARE_METATYPE(TCDBus::Result<QString>)
code is stored in 'value' */
class DynamicReadableAdaptor : public QDBusAbstractAdaptor {
public:
explicit DynamicReadableAdaptor(QObject *obj, DynamicReadable dr) :
QDBusAbstractAdaptor(obj) {
// Ideally this should be moved somewhere else but QMetaType does not handle namespaces well
explicit DynamicReadableAdaptor(QObject *obj, DynamicReadable dr)
: QDBusAbstractAdaptor(obj) {
// Ideally this should be moved somewhere else but QMetaType does not handle
// namespaces well
qDBusRegisterMetaType<TCDBus::Result<QDBusVariant>>();
qDBusRegisterMetaType<TCDBus::Result<QString>>();
m_dynamicReadable = dr;
}
// Might not have a unit
TCDBus::Result<QString> unit_() {
return (m_dynamicReadable.unit().has_value()) ?
TCDBus::Result<QString>{
false,
QString::fromStdString(m_dynamicReadable.unit().value())} :
TCDBus::Result<QString>{true, QString("")};
return (m_dynamicReadable.unit().has_value())
? TCDBus::Result<QString>{false,
QString::fromStdString(m_dynamicReadable.unit().value())}
: TCDBus::Result<QString>{true, QString("")};
}
public Q_SLOTS:
TCDBus::Result<QDBusVariant> value() {
QVariant v;
TCDBus::Result<QDBusVariant> res{.error = false};
/* We have to unwrap the value here, another option would be to convert the std::variant
* to char*, but that comes with its own issues */
match(m_dynamicReadable.read())
(pattern(as<ReadableValue>(arg)) = [&](auto val) {
match(val)
(pattern(as<uint>(arg)) = [&](auto u) {
v.setValue(u);
},
pattern(as<double>(arg)) = [&](auto d) {
v.setValue(d);
});
/* We have to unwrap the value here, another option would be to convert the
* std::variant to char*, but that comes with its own issues */
match(m_dynamicReadable.read())(
pattern(as<ReadableValue>(arg)) =
[&](auto val) {
match(val)(
pattern(as<uint>(arg)) = [&](auto u) { v.setValue(u); },
pattern(as<double>(arg)) = [&](auto d) { v.setValue(d); });
},
pattern(as<ReadError>(arg)) = [&](auto err) {
pattern(as<ReadError>(arg)) =
[&](auto err) {
v.setValue(static_cast<int>(err));
res.error = true;
});
});
res.value = QDBusVariant(v);
return res;
}
@@ -67,7 +65,7 @@ private:
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.tuxclocker.DynamicReadable")
Q_PROPERTY(TCDBus::Result<QString> unit READ unit_)
DynamicReadable m_dynamicReadable;
};
@@ -79,8 +77,8 @@ Q_DECLARE_METATYPE(TCDBus::Result<uint>)
class AssignableAdaptor : public QDBusAbstractAdaptor {
public:
explicit AssignableAdaptor(QObject *obj, Assignable a) :
QDBusAbstractAdaptor(obj), m_assignable(a) {
explicit AssignableAdaptor(QObject *obj, Assignable a)
: QDBusAbstractAdaptor(obj), m_assignable(a) {
qDBusRegisterMetaType<TCDBus::Range>();
qDBusRegisterMetaType<TCDBus::Enumeration>();
qDBusRegisterMetaType<QVector<TCDBus::Enumeration>>();
@@ -92,108 +90,95 @@ public:
QVariant a_info;
// Unwrap AssignableInfo :(
match(a.assignableInfo())
(pattern(as<RangeInfo>(arg)) = [&](auto r_info) {
match(r_info)
(pattern(as<Range<double>>(arg)) = [&](auto dr) {
match(a.assignableInfo())(
pattern(as<RangeInfo>(arg)) =
[&](auto r_info) {
match(r_info)(
pattern(as<Range<double>>(arg)) =
[&](auto dr) {
TCDBus::Range r{
.min = QDBusVariant(QVariant(dr.min)),
.max = QDBusVariant(QVariant(dr.max))
};
.min = QDBusVariant(QVariant(dr.min)),
.max = QDBusVariant(QVariant(dr.max))};
a_info.setValue(r);
},
pattern(as<Range<int>>(arg)) = [&](auto ir) {
pattern(as<Range<int>>(arg)) =
[&](auto ir) {
TCDBus::Range r{
.min = QDBusVariant(QVariant(ir.min)),
.max = QDBusVariant(QVariant(ir.max))
};
.min = QDBusVariant(QVariant(ir.min)),
.max = QDBusVariant(QVariant(ir.max))};
a_info.setValue(r);
});
});
},
pattern(as<EnumerationVec>(arg)) = [&](auto enums) {
pattern(as<EnumerationVec>(arg)) =
[&](auto enums) {
QVector<TCDBus::Enumeration> entries;
for (const auto &e : enums)
entries.append({e.key, QString::fromStdString(e.name)});
a_info.setValue(entries);
});
});
m_dbusAssignableInfo = QDBusVariant(a_info);
}
QDBusVariant assignableInfo_() {return m_dbusAssignableInfo;}
QDBusVariant assignableInfo_() { return m_dbusAssignableInfo; }
TCDBus::Result<QString> unit_() {
if (m_assignable.unit().has_value()) {
return TCDBus::Result<QString> {
.error = false,
.value = QString::fromStdString(m_assignable.unit().value())
};
return TCDBus::Result<QString>{.error = false,
.value = QString::fromStdString(m_assignable.unit().value())};
}
return TCDBus::Result<QString> {
.error = true,
.value = QString("")
};
return TCDBus::Result<QString>{.error = true, .value = QString("")};
}
public Q_SLOTS:
TCDBus::Result<QDBusVariant> currentValue() {
// Indicate error by default
TCDBus::Result<QDBusVariant> retval {
.error = true,
.value = QDBusVariant(QVariant(0))
};
match(m_assignable.currentValue())
(pattern(some(arg)) = [&](auto aa) {
retval.error = false;
match(aa)
(pattern(as<double>(arg)) = [&](auto d) {
retval.value = QDBusVariant(QVariant(d));
},
pattern(as<int>(arg)) = [&](auto i) {
retval.value = QDBusVariant(QVariant(i));
},
pattern(as<uint>(arg)) = [&](auto u) {
retval.value = QDBusVariant(QVariant(u));
}
);
TCDBus::Result<QDBusVariant> retval{
.error = true, .value = QDBusVariant(QVariant(0))};
match(m_assignable.currentValue())(
pattern(some(arg)) =
[&](auto aa) {
retval.error = false;
match(aa)(
pattern(as<double>(arg)) =
[&](auto d) { retval.value = QDBusVariant(QVariant(d)); },
pattern(as<int>(arg)) =
[&](auto i) { retval.value = QDBusVariant(QVariant(i)); },
pattern(as<uint>(arg)) =
[&](auto u) { retval.value = QDBusVariant(QVariant(u)); });
},
pattern(none) = [] {}
);
pattern(none) = [] {});
return retval;
}
TCDBus::Result<int> assign(QDBusVariant arg_) {
auto v = arg_.variant();
std::optional<AssignmentError> retval;
match(m_assignable.assignableInfo())
(pattern(as<RangeInfo>(arg)) = [&](auto ri) {
match(ri)
(pattern(as<Range<double>>(_)) = [&] {
retval = m_assignable.assign(v.value<double>());
},
pattern(as<Range<int>>(_)) = [&] {
retval = m_assignable.assign(v.value<int>());
});
match(m_assignable.assignableInfo())(
pattern(as<RangeInfo>(arg)) =
[&](auto ri) {
match(ri)(
pattern(as<Range<double>>(_)) =
[&] { retval = m_assignable.assign(v.value<double>()); },
pattern(as<Range<int>>(_)) =
[&] { retval = m_assignable.assign(v.value<int>()); });
},
pattern(as<EnumerationVec>(_)) = [&] {
retval = m_assignable.assign(v.value<uint>());
});
pattern(as<EnumerationVec>(_)) =
[&] { retval = m_assignable.assign(v.value<uint>()); });
TCDBus::Result<int> res{.error = false, 0};
// Check if optional contains error
if (retval.has_value()) {
res.error = true;
res.value = static_cast<int>(retval.value());
}
return res;
}
private:
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.tuxclocker.Assignable")
Q_PROPERTY(QDBusVariant assignableInfo READ assignableInfo_)
Q_PROPERTY(TCDBus::Result<QString> unit READ unit_)
Assignable m_assignable;
QDBusVariant m_dbusAssignableInfo;
};
@@ -204,43 +189,44 @@ Q_DECLARE_METATYPE(TCDBus::FlatTreeNode<TCDBus::DeviceNode>)
// Holds the name and hash of nodes, even if they don't implement an interface
class NodeAdaptor : public QDBusAbstractAdaptor {
public:
NodeAdaptor(QObject *obj, DeviceNode devNode) : QDBusAbstractAdaptor(obj),
m_devNode(devNode) {}
QString name_() {return QString::fromStdString(m_devNode.name);}
QString hash_() {return QString::fromStdString(m_devNode.hash);}
NodeAdaptor(QObject *obj, DeviceNode devNode)
: QDBusAbstractAdaptor(obj), m_devNode(devNode) {}
QString name_() { return QString::fromStdString(m_devNode.name); }
QString hash_() { return QString::fromStdString(m_devNode.hash); }
private:
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.tuxclocker.Node")
Q_PROPERTY(QString hash READ hash_)
Q_PROPERTY(QString name READ name_)
DeviceNode m_devNode;
};
// Holds the main tree and returns it as a list (because parsing XML sucks)
class MainAdaptor : public QDBusAbstractAdaptor {
public:
explicit MainAdaptor(QObject *obj, TreeNode<TCDBus::DeviceNode> node) : QDBusAbstractAdaptor(obj) {
explicit MainAdaptor(QObject *obj, TreeNode<TCDBus::DeviceNode> node)
: QDBusAbstractAdaptor(obj) {
qDBusRegisterMetaType<TCDBus::DeviceNode>();
qDBusRegisterMetaType<TCDBus::FlatTreeNode<TCDBus::DeviceNode>>();
qDBusRegisterMetaType<QVector<TCDBus::FlatTreeNode<TCDBus::DeviceNode>>>();
for (const auto &f_node : node.toFlatTree().nodes) {
// Copy child indices
QVector<int> childIndices;
for (const auto &i : f_node.childIndices)
childIndices.append(i);
TCDBus::FlatTreeNode<TCDBus::DeviceNode> fn{f_node.value, childIndices};
m_flatTree.append(fn);
}
}
public Q_SLOTS:
QVector<TCDBus::FlatTreeNode<TCDBus::DeviceNode>> flatDeviceTree() {return m_flatTree;}
QVector<TCDBus::FlatTreeNode<TCDBus::DeviceNode>> flatDeviceTree() { return m_flatTree; }
private:
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.tuxclocker")
TreeNode<TCDBus::DeviceNode> m_rootNode;
QVector<TCDBus::FlatTreeNode<TCDBus::DeviceNode>> m_flatTree;
};
@@ -248,30 +234,26 @@ private:
class StaticReadableAdaptor : public QDBusAbstractAdaptor {
public:
StaticReadableAdaptor(QObject *obj, StaticReadable readable)
: QDBusAbstractAdaptor(obj), m_readable(readable) {
: QDBusAbstractAdaptor(obj), m_readable(readable) {
qDBusRegisterMetaType<TCDBus::Result<QString>>();
// Unwrap the value and store in QDBusVariant
match(m_readable.value()) (
pattern(as<uint>(arg)) = [this](auto i) {
m_value = QDBusVariant(QVariant(i));
}
);
m_unit = (m_readable.unit().has_value()) ? TCDBus::Result<QString>{
false,
QString::fromStdString(m_readable.unit().value())
} : TCDBus::Result<QString>{
true, ""
};
match(m_readable.value())(pattern(as<uint>(arg)) = [this](auto i) {
m_value = QDBusVariant(QVariant(i));
});
m_unit = (m_readable.unit().has_value())
? TCDBus::Result<QString>{false,
QString::fromStdString(m_readable.unit().value())}
: TCDBus::Result<QString>{true, ""};
}
QDBusVariant value_() {return m_value;}
TCDBus::Result<QString> unit_() {return m_unit;}
QDBusVariant value_() { return m_value; }
TCDBus::Result<QString> unit_() { return m_unit; }
private:
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.tuxclocker.StaticReadable")
Q_PROPERTY(TCDBus::Result<QString> unit READ unit_)
Q_PROPERTY(QDBusVariant value READ value_)
StaticReadable m_readable;
TCDBus::Result<QString> m_unit;
QDBusVariant m_value;

View File

@@ -5,19 +5,20 @@
#include <QDebug>
QDBusAbstractAdaptor *AssignableAdaptorFactory::assignableAdaptor(QObject *parent, const tc_assignable_node_t *node) {
QDBusAbstractAdaptor *AssignableAdaptorFactory::assignableAdaptor(
QObject *parent, const tc_assignable_node_t *node) {
if (!node->assign_callback) {
// Placeholder node
return nullptr;
}
switch (node->value_category) {
case TC_ASSIGNABLE_RANGE:
return new AssignableVariantRangeAdaptor(parent, node);
case TC_ASSIGNABLE_ENUM:
return new AssignableEnumAdaptor(parent, node);
default:
return nullptr;
case TC_ASSIGNABLE_RANGE:
return new AssignableVariantRangeAdaptor(parent, node);
case TC_ASSIGNABLE_ENUM:
return new AssignableEnumAdaptor(parent, node);
default:
return nullptr;
}
return nullptr;
}

View File

@@ -5,5 +5,6 @@
class AssignableAdaptorFactory {
public:
static QDBusAbstractAdaptor *assignableAdaptor(QObject *parent, const tc_assignable_node_t *node);
static QDBusAbstractAdaptor *assignableAdaptor(
QObject *parent, const tc_assignable_node_t *node);
};

View File

@@ -1,17 +1,15 @@
#include "AssignableEnumAdaptor.h"
AssignableEnumAdaptor::AssignableEnumAdaptor(QObject *obj, const tc_assignable_node_t* node) : QDBusAbstractAdaptor(obj) {
AssignableEnumAdaptor::AssignableEnumAdaptor(QObject *obj, const tc_assignable_node_t *node)
: QDBusAbstractAdaptor(obj) {
m_node = node;
}
QList <EnumData> AssignableEnumAdaptor::values_() {
QList <EnumData> retval;
QList<EnumData> AssignableEnumAdaptor::values_() {
QList<EnumData> retval;
for (uint16_t i = 0; i < m_node->enum_info.property_count; i++) {
EnumData ed = {
.name = QString(m_node->enum_info.properties[i]),
.value = i
};
EnumData ed = {.name = QString(m_node->enum_info.properties[i]), .value = i};
retval.append(ed);
}
return retval;
@@ -19,11 +17,11 @@ QList <EnumData> AssignableEnumAdaptor::values_() {
short AssignableEnumAdaptor::assign(ushort value) {
tc_variant_t tc_v;
tc_v.uint_value = value;
tc_v.data_type = TC_TYPE_UINT;
assert(m_node->assign_callback);
return m_node->assign_callback(tc_v, m_node);
}

View File

@@ -7,16 +7,16 @@
class AssignableEnumAdaptor : public QDBusAbstractAdaptor {
public:
explicit AssignableEnumAdaptor(QObject *obj, const tc_assignable_node_t* node);
QList <EnumData> values_();
QString name_() {return QString(m_node->name);}
explicit AssignableEnumAdaptor(QObject *obj, const tc_assignable_node_t *node);
QList<EnumData> values_();
QString name_() { return QString(m_node->name); }
public Q_SLOTS:
short assign(ushort value);
private:
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.tuxclocker.Assignable.Enum")
Q_PROPERTY(QList <EnumData> values READ values_)
Q_PROPERTY(QList<EnumData> values READ values_)
Q_PROPERTY(QString name READ name_)
const tc_assignable_node_t *m_node;
};

View File

@@ -1,24 +1,26 @@
#include "AssignableVariantRangeAdaptor.h"
AssignableVariantRangeAdaptor::AssignableVariantRangeAdaptor(QObject *obj, const tc_assignable_node_t *node) : QDBusAbstractAdaptor(obj) {
AssignableVariantRangeAdaptor::AssignableVariantRangeAdaptor(
QObject *obj, const tc_assignable_node_t *node)
: QDBusAbstractAdaptor(obj) {
m_node = node;
}
QDBusVariant AssignableVariantRangeAdaptor::min_() {
QVariant v;
QDBusVariant dv;
switch (m_node->range_info.range_data_type) {
case TC_ASSIGNABLE_RANGE_INT: {
qlonglong val = m_node->range_info.int_range.min;
v.setValue(val);
break;
}
case TC_ASSIGNABLE_RANGE_DOUBLE:
v.setValue(m_node->range_info.double_range.min);
break;
default:
break;
case TC_ASSIGNABLE_RANGE_INT: {
qlonglong val = m_node->range_info.int_range.min;
v.setValue(val);
break;
}
case TC_ASSIGNABLE_RANGE_DOUBLE:
v.setValue(m_node->range_info.double_range.min);
break;
default:
break;
}
dv.setVariant(v);
return dv;
@@ -27,18 +29,18 @@ QDBusVariant AssignableVariantRangeAdaptor::min_() {
QDBusVariant AssignableVariantRangeAdaptor::max_() {
QVariant v;
QDBusVariant dv;
switch (m_node->range_info.range_data_type) {
case TC_ASSIGNABLE_RANGE_INT: {
qlonglong val = m_node->range_info.int_range.max;
v.setValue(val);
break;
}
case TC_ASSIGNABLE_RANGE_DOUBLE:
v.setValue(m_node->range_info.double_range.max);
break;
default:
break;
case TC_ASSIGNABLE_RANGE_INT: {
qlonglong val = m_node->range_info.int_range.max;
v.setValue(val);
break;
}
case TC_ASSIGNABLE_RANGE_DOUBLE:
v.setValue(m_node->range_info.double_range.max);
break;
default:
break;
}
dv.setVariant(v);
return dv;
@@ -46,21 +48,23 @@ QDBusVariant AssignableVariantRangeAdaptor::max_() {
short AssignableVariantRangeAdaptor::assign(QDBusVariant value) {
auto v = value.variant();
// Convert to tc_variant_t
tc_variant_t tc_v;
if (v.canConvert<qlonglong>() && m_node->range_info.range_data_type == TC_ASSIGNABLE_RANGE_INT) {
if (v.canConvert<qlonglong>() &&
m_node->range_info.range_data_type == TC_ASSIGNABLE_RANGE_INT) {
tc_v.int_value = qvariant_cast<qlonglong>(v);
tc_v.data_type = TC_TYPE_INT;
return m_node->assign_callback(tc_v, m_node);
}
if (v.canConvert<double>() && m_node->range_info.range_data_type == TC_ASSIGNABLE_RANGE_DOUBLE) {
if (v.canConvert<double>() &&
m_node->range_info.range_data_type == TC_ASSIGNABLE_RANGE_DOUBLE) {
tc_v.double_value = qvariant_cast<double>(v);
tc_v.data_type = TC_TYPE_DOUBLE;
return m_node->assign_callback(tc_v, m_node);
}
return TC_EGENERIC;
}

View File

@@ -7,8 +7,8 @@
class AssignableVariantRangeAdaptor : public QDBusAbstractAdaptor {
public:
explicit AssignableVariantRangeAdaptor(QObject *obj, const tc_assignable_node_t *node);
QString name_() {return QString(m_node->name);}
QString unit_() {return QString(m_node->unit);}
QString name_() { return QString(m_node->name); }
QString unit_() { return QString(m_node->unit); }
QDBusVariant min_();
QDBusVariant max_();
public Q_SLOTS:
@@ -20,6 +20,6 @@ private:
Q_PROPERTY(QString unit READ unit_)
Q_PROPERTY(QDBusVariant min READ min_)
Q_PROPERTY(QDBusVariant max READ max_)
const tc_assignable_node_t *m_node;
};

View File

@@ -6,7 +6,7 @@
struct EnumData {
QString name;
unsigned value;
friend QDBusArgument &operator<<(QDBusArgument &arg, const EnumData &data) {
arg.beginStructure();
arg << data.name << data.value;
@@ -21,18 +21,18 @@ struct EnumData {
}
/*friend QDBusArgument &operator<<(QDBusArgument &arg, const QList <EnumData> list) {
arg.beginArray();
for (auto data : list) {
arg << data;
}
arg.endArray();
return arg;
}
}
friend const QDBusArgument &operator>>(const QDBusArgument &arg, QList <EnumData> &list) {
arg.beginArray();
list.clear();
while (!arg.atEnd()) {
EnumData data;
arg >> data;

View File

@@ -1,6 +1,7 @@
#include "ReadableAdaptorFactory.h"
QDBusAbstractAdaptor *ReadableAdaptorFactory::readableAdaptor(QObject *parent, const tc_readable_node_t *node) {
QDBusAbstractAdaptor *ReadableAdaptorFactory::readableAdaptor(
QObject *parent, const tc_readable_node_t *node) {
if (!node->value_callback) {
return nullptr;
}

View File

@@ -8,5 +8,6 @@
class ReadableAdaptorFactory {
public:
static QDBusAbstractAdaptor *readableAdaptor(QObject *parent, const tc_readable_node_t *node);
static QDBusAbstractAdaptor *readableAdaptor(
QObject *parent, const tc_readable_node_t *node);
};

View File

@@ -1,36 +1,35 @@
#include "ReadableDynamicVariantAdaptor.h"
ReadableDynamicVariantAdaptor::ReadableDynamicVariantAdaptor(QObject *obj, const tc_readable_node_t *node) : QDBusAbstractAdaptor(obj) {
ReadableDynamicVariantAdaptor::ReadableDynamicVariantAdaptor(
QObject *obj, const tc_readable_node_t *node)
: QDBusAbstractAdaptor(obj) {
m_node = node;
}
Result <QDBusVariant> ReadableDynamicVariantAdaptor::value() {
Result<QDBusVariant> ReadableDynamicVariantAdaptor::value() {
QDBusVariant dv;
QVariant v;
auto result = m_node->value_callback(m_node);
switch (result.data.data_type) {
case TC_TYPE_DOUBLE:
v.setValue(result.data.double_value);
break;
case TC_TYPE_INT:
v.setValue(result.data.int_value);
break;
case TC_TYPE_UINT: {
qulonglong value = result.data.uint_value;
v.setValue(value);
break;
}
default:
break;
case TC_TYPE_DOUBLE:
v.setValue(result.data.double_value);
break;
case TC_TYPE_INT:
v.setValue(result.data.int_value);
break;
case TC_TYPE_UINT: {
qulonglong value = result.data.uint_value;
v.setValue(value);
break;
}
default:
break;
}
dv.setVariant(v);
Result <QDBusVariant> res = {
.valid = result.valid,
.value = dv
};
Result<QDBusVariant> res = {.valid = result.valid, .value = dv};
return res;
}

View File

@@ -8,13 +8,13 @@
class ReadableDynamicVariantAdaptor : public QDBusAbstractAdaptor {
public:
explicit ReadableDynamicVariantAdaptor(QObject *obj, const tc_readable_node_t *node);
QString name_() {return QString(m_node->name);}
QString name_() { return QString(m_node->name); }
public Q_SLOTS:
Result <QDBusVariant> value();
Result<QDBusVariant> value();
private:
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.tuxclocker.Readable.Dynamic.Variant")
Q_PROPERTY(QString name READ name_)
const tc_readable_node_t *m_node;
};

View File

@@ -5,23 +5,22 @@
#include <QDBusArgument>
#include <QDBusMetaType>
template <typename T>
struct Result {
bool valid;
T value;
friend QDBusArgument &operator<<(QDBusArgument &arg, const Result &res) {
arg.beginStructure();
arg << res.valid << res.value;
arg.endStructure();
return arg;
}
template <typename T> struct Result {
bool valid;
T value;
friend QDBusArgument &operator<<(QDBusArgument &arg, const Result &res) {
arg.beginStructure();
arg << res.valid << res.value;
arg.endStructure();
return arg;
}
friend const QDBusArgument &operator>>(const QDBusArgument &arg, Result &varRes) {
arg.beginStructure();
arg >> varRes.valid >> varRes.value;
arg.endStructure();
return arg;
}
friend const QDBusArgument &operator>>(const QDBusArgument &arg, Result &varRes) {
arg.beginStructure();
arg >> varRes.valid >> varRes.value;
arg.endStructure();
return arg;
}
};
Q_DECLARE_METATYPE(Result<QDBusVariant>)

View File

@@ -20,75 +20,72 @@ namespace TCDBus = TuxClocker::DBus;
int main(int argc, char **argv) {
QCoreApplication a(argc, argv);
auto connection = QDBusConnection::systemBus();
auto plugins = DevicePlugin::loadPlugins();
QVector<QDBusAbstractAdaptor*> adaptors;
QVector<QDBusAbstractAdaptor *> adaptors;
QObject root;
TreeNode<TCDBus::DeviceNode> dbusRootNode;
std::function<void(TreeNode<DeviceNode>, QString, TreeNode<TCDBus::DeviceNode>*)> traverse;
traverse = [&traverse, &connection, &adaptors, &root](
TreeNode<DeviceNode> node,
QString parentPath, TreeNode<TCDBus::DeviceNode> *dbusNode) {
std::function<void(TreeNode<DeviceNode>, QString, TreeNode<TCDBus::DeviceNode> *)> traverse;
traverse = [&traverse, &connection, &adaptors, &root](TreeNode<DeviceNode> node,
QString parentPath, TreeNode<TCDBus::DeviceNode> *dbusNode) {
auto obj = new QObject(&root); // Is destroyed when root goes out of scope
auto objName = QString::fromStdString(node.value().hash).replace(" ", "");
auto thisPath = parentPath + objName;
QString ifaceName;
adaptors.append(new NodeAdaptor(obj, node.value()));
if_let(pattern(some(arg)) = node.value().interface) = [&](auto iface) {
if_let(pattern(some(arg)) =
AdaptorFactory::adaptor(obj, iface)) = [&](auto adaptor) {
if_let(pattern(some(arg)) =
AdaptorFactory::adaptor(obj, iface)) = [&](auto adaptor) {
adaptors.append(adaptor);
// TODO: don't hardcode interface name
// Maybe create an intermediate class that contains the interface name to avoid this
// For some reason this throws a bad_cast when using 'as' with pointer type
match(*adaptor)
(pattern(as<AssignableAdaptor>(_)) = [&] {
ifaceName = "org.tuxclocker.Assignable";
},
pattern(as<DynamicReadableAdaptor>(_)) = [&] {
ifaceName = "org.tuxclocker.DynamicReadable";
},
pattern(as<StaticReadableAdaptor>(_)) = [&] {
ifaceName = "org.tuxclocker.StaticReadable";
},
pattern(_) = [] {});
// Maybe create an intermediate class that contains the interface
// name to avoid this For some reason this throws a bad_cast when
// using 'as' with pointer type
match (*adaptor)(
pattern(as<AssignableAdaptor>(_)) =
[&] { ifaceName = "org.tuxclocker.Assignable"; },
pattern(as<DynamicReadableAdaptor>(_)) =
[&] { ifaceName = "org.tuxclocker.DynamicReadable"; },
pattern(as<StaticReadableAdaptor>(_)) =
[&] { ifaceName = "org.tuxclocker.StaticReadable"; },
pattern(_) = [] {});
};
};
if (!connection.registerObject(thisPath, obj))
qDebug() << "Couldn't register object at path" << thisPath << connection.lastError();
qDebug() << "Couldn't register object at path" << thisPath
<< connection.lastError();
auto thisDBusNode = TreeNode<TCDBus::DeviceNode>{{ifaceName, thisPath}};
dbusNode->appendChild(thisDBusNode);
qDebug() << thisPath;
for (const auto &child : node.children())
traverse(child, thisPath + "/", &dbusNode->childrenPtr()->back());
};
TreeNode<DeviceNode> lvl1nodes;
if (plugins.has_value()) {
qDebug() << "found " << plugins.value().size() << " plugins";
for (auto &plugin : plugins.value()) {
//rootNodes.append(plugin->deviceRootNode());
// Root node should always be empty
// rootNodes.append(plugin->deviceRootNode());
// Root node should always be empty
for (const auto &node : plugin->deviceRootNode().children()) {
lvl1nodes.appendChild(node);
traverse(node, "/", &dbusRootNode);
TreeNode<DeviceNode>::preorder(node, [](auto val) {
qDebug() << QString::fromStdString(val.name);
});
TreeNode<DeviceNode>::preorder(node,
[](auto val) { qDebug() << QString::fromStdString(val.name); });
}
}
}
auto ma = new MainAdaptor(&root, dbusRootNode);
connection.registerObject("/", &root);
if (!connection.registerService("org.tuxclocker")) {
qDebug() << "unable to register:" << connection.lastError().message();
qDebug() << "errcode" << connection.lastError().type();
}
return a.exec();
}