Initial Commit
This commit is contained in:
commit
f4a1b3264a
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
build/
|
29
.vscode/launch.json
vendored
Normal file
29
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "clang - Build and debug active file",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${fileDirname}/${fileBasenameNoExtension}",
|
||||||
|
"args": [],
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"environment": [],
|
||||||
|
"externalConsole": false,
|
||||||
|
"MIMode": "lldb",
|
||||||
|
"setupCommands": [
|
||||||
|
{
|
||||||
|
"description": "Enable pretty-printing for gdb",
|
||||||
|
"text": "-enable-pretty-printing",
|
||||||
|
"ignoreFailures": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preLaunchTask": "C/C++: clang build active file",
|
||||||
|
"miDebuggerPath": "/usr/bin/lldb-mi"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
7
.vscode/settings.json
vendored
Normal file
7
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"cmake.configureOnOpen": true,
|
||||||
|
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
|
||||||
|
"files.associations": {
|
||||||
|
"limits": "c"
|
||||||
|
}
|
||||||
|
}
|
26
.vscode/tasks.json
vendored
Normal file
26
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"type": "shell",
|
||||||
|
"label": "C/C++: clang build active file",
|
||||||
|
"command": "/usr/bin/clang",
|
||||||
|
"args": [
|
||||||
|
"-g",
|
||||||
|
"${file}",
|
||||||
|
"-o",
|
||||||
|
"${fileDirname}/${fileBasenameNoExtension}"
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
"problemMatcher": [
|
||||||
|
"$gcc"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": "2.0.0"
|
||||||
|
}
|
37
CMakeLists.txt
Normal file
37
CMakeLists.txt
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.1.0)
|
||||||
|
|
||||||
|
project(feedbackd-configuration)
|
||||||
|
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
|
||||||
|
pkg_check_modules(GTK3 REQUIRED IMPORTED_TARGET gtk+-3.0)
|
||||||
|
pkg_check_modules(LIBHANDY REQUIRED IMPORTED_TARGET libhandy-1)
|
||||||
|
pkg_check_modules(JSON_GLIB REQUIRED IMPORTED_TARGET json-glib-1.0)
|
||||||
|
|
||||||
|
link_libraries(PkgConfig::GTK3 PkgConfig::LIBHANDY PkgConfig::JSON_GLIB)
|
||||||
|
|
||||||
|
add_compile_options(-Wall -Wextra -Werror)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${CMAKE_BINARY_DIR}/.gresource.c" non-existent.c
|
||||||
|
COMMAND glib-compile-resources
|
||||||
|
ARGS --target "${CMAKE_BINARY_DIR}/.gresource.c" --generate-source --sourcedir "${CMAKE_SOURCE_DIR}/src" "${CMAKE_SOURCE_DIR}/src/.gresource.xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
function(add_debug_flag x)
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
add_compile_options("${x}")
|
||||||
|
add_link_options("${x}")
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
add_debug_flag("-fno-omit-frame-pointer;-fsanitize=address")
|
||||||
|
|
||||||
|
add_executable(feedbackd-configuration "${CMAKE_BINARY_DIR}/.gresource.c" src/main.c src/dialog.c src/util.c)
|
||||||
|
|
||||||
|
install(TARGETS feedbackd-configuration)
|
||||||
|
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
|
install(FILES "data/com.thebrokenrail.FeedbackD-Configuration.desktop" DESTINATION "${CMAKE_INSTALL_FULL_DATAROOTDIR}/applications")
|
||||||
|
install(FILES "data/com.thebrokenrail.FeedbackD-Configuration.png" DESTINATION "${CMAKE_INSTALL_FULL_DATAROOTDIR}/icons/hicolor/96x96/apps")
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 TheBrokenRail
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
2
README.md
Normal file
2
README.md
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# FeedbackD Configuration
|
||||||
|
A Simple Configuration Tool For [FeedbackD](https://source.puri.sm/Librem5/feedbackd)
|
8
data/com.thebrokenrail.FeedbackD-Configuration.desktop
Normal file
8
data/com.thebrokenrail.FeedbackD-Configuration.desktop
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Name=FeedbackD Configuration
|
||||||
|
Exec=feedbackd-configuration
|
||||||
|
Icon=com.thebrokenrail.FeedbackD-Configuration
|
||||||
|
Terminal=false
|
||||||
|
Type=Application
|
||||||
|
Categories=GTK;
|
||||||
|
StartupNotify=true
|
BIN
data/com.thebrokenrail.FeedbackD-Configuration.png
Normal file
BIN
data/com.thebrokenrail.FeedbackD-Configuration.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 KiB |
7
src/.gresource.xml
Normal file
7
src/.gresource.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<gresources>
|
||||||
|
<gresource>
|
||||||
|
<file preprocess="xml-stripblanks">main_window.glade</file>
|
||||||
|
<file preprocess="xml-stripblanks">dialog_window.glade</file>
|
||||||
|
</gresource>
|
||||||
|
</gresources>
|
165
src/dialog.c
Normal file
165
src/dialog.c
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
#include <gtk/gtk.h>
|
||||||
|
#include <json-glib/json-glib.h>
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
// Context For The Save/Modify Event Dialog
|
||||||
|
struct save_event_data {
|
||||||
|
struct event_location *location;
|
||||||
|
GtkWindow *window;
|
||||||
|
GtkEntry *name;
|
||||||
|
GtkStack *stack;
|
||||||
|
// Sound
|
||||||
|
GtkWidget *sound;
|
||||||
|
GtkEntry *sound_name;
|
||||||
|
// Vibration (Rumble)
|
||||||
|
GtkWidget *vibration_rumble;
|
||||||
|
GtkEntry *vibration_rumble_duration;
|
||||||
|
// Vibration (Periodic)
|
||||||
|
GtkWidget *vibration_periodic;
|
||||||
|
GtkEntry *magnitude;
|
||||||
|
GtkEntry *vibration_periodic_duration;
|
||||||
|
GtkEntry *fade_in_time;
|
||||||
|
GtkEntry *fade_in_level;
|
||||||
|
// LED
|
||||||
|
GtkWidget *led;
|
||||||
|
GtkEntry *color;
|
||||||
|
GtkEntry *frequency;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Save Event Handler
|
||||||
|
static void on_save_event(__attribute__((unused)) GtkButton *button, struct save_event_data *data) {
|
||||||
|
JsonObject *event = json_object_new();
|
||||||
|
int free_obj = 0;
|
||||||
|
if (data->location->id != -1) {
|
||||||
|
JsonNode *node = json_array_get_element(data->location->profile, data->location->id);
|
||||||
|
json_node_set_object(node, event);
|
||||||
|
free_obj = 1;
|
||||||
|
} else {
|
||||||
|
json_array_add_object_element(data->location->profile, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object_set_string_member(event, "event-name", gtk_entry_get_text(data->name));
|
||||||
|
|
||||||
|
GtkWidget *visible_widget = gtk_stack_get_visible_child(data->stack);
|
||||||
|
|
||||||
|
if (visible_widget == data->sound) {
|
||||||
|
json_object_set_string_member(event, "type", "Sound");
|
||||||
|
json_object_set_string_member(event, "effect", gtk_entry_get_text(data->sound_name));
|
||||||
|
} else if (visible_widget == data->vibration_rumble) {
|
||||||
|
json_object_set_string_member(event, "type", "VibraRumble");
|
||||||
|
json_object_set_double_member(event, "duration", g_strtod(gtk_entry_get_text(data->vibration_rumble_duration), NULL));
|
||||||
|
} else if (visible_widget == data->vibration_periodic) {
|
||||||
|
json_object_set_string_member(event, "type", "VibraPeriodic");
|
||||||
|
json_object_set_double_member(event, "magnitude", g_strtod(gtk_entry_get_text(data->magnitude), NULL));
|
||||||
|
json_object_set_double_member(event, "duration", g_strtod(gtk_entry_get_text(data->vibration_periodic_duration), NULL));
|
||||||
|
json_object_set_double_member(event, "fade-in-time", g_strtod(gtk_entry_get_text(data->fade_in_time), NULL));
|
||||||
|
json_object_set_double_member(event, "fade-in-level", g_strtod(gtk_entry_get_text(data->fade_in_level), NULL));
|
||||||
|
} else if (visible_widget == data->led) {
|
||||||
|
json_object_set_string_member(event, "type", "Led");
|
||||||
|
json_object_set_string_member(event, "color", gtk_entry_get_text(data->color));
|
||||||
|
json_object_set_double_member(event, "frequency", g_strtod(gtk_entry_get_text(data->frequency), NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_window_close(GTK_WINDOW(data->window));
|
||||||
|
|
||||||
|
reload_profiles(1);
|
||||||
|
|
||||||
|
if (free_obj) {
|
||||||
|
json_object_unref(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets Long In GtkEntry Text Field
|
||||||
|
static void set_entry_long(GtkEntry *entry, long x) {
|
||||||
|
gchar *str = g_strdup_printf("%ld", x);
|
||||||
|
gtk_entry_set_text(entry, str);
|
||||||
|
g_free(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-Fill Dialog With Existing Event Data
|
||||||
|
static void apply_event_to_dialog(struct save_event_data *data) {
|
||||||
|
if (data->location->id != -1) {
|
||||||
|
JsonObject *event = json_array_get_object_element(data->location->profile, data->location->id);
|
||||||
|
|
||||||
|
gtk_entry_set_text(data->name, json_object_get_string_member(event, "event-name"));
|
||||||
|
|
||||||
|
const gchar *type = json_object_get_string_member(event, "type");
|
||||||
|
|
||||||
|
if (g_strcmp0(type, "Sound") == 0) {
|
||||||
|
gtk_stack_set_visible_child(data->stack, data->sound);
|
||||||
|
gtk_entry_set_text(data->sound_name, json_object_get_string_member(event, "effect"));
|
||||||
|
} else if (g_strcmp0(type, "VibraRumble") == 0) {
|
||||||
|
gtk_stack_set_visible_child(data->stack, data->vibration_rumble);
|
||||||
|
set_entry_long(data->vibration_rumble_duration, json_object_get_int_member(event, "duration"));
|
||||||
|
} else if (g_strcmp0(type, "VibraPeriodic") == 0) {
|
||||||
|
gtk_stack_set_visible_child(data->stack, data->vibration_periodic);
|
||||||
|
set_entry_long(data->magnitude, json_object_get_int_member(event, "magnitude"));
|
||||||
|
set_entry_long(data->vibration_periodic_duration, json_object_get_int_member(event, "duration"));
|
||||||
|
set_entry_long(data->fade_in_time, json_object_get_int_member(event, "fade-in-time"));
|
||||||
|
set_entry_long(data->fade_in_level, json_object_get_int_member(event, "fade-in-level"));
|
||||||
|
} else if (g_strcmp0(type, "Led") == 0) {
|
||||||
|
gtk_stack_set_visible_child(data->stack, data->led);
|
||||||
|
gtk_entry_set_text(data->color, json_object_get_string_member(event, "color"));
|
||||||
|
set_entry_long(data->frequency, json_object_get_int_member(event, "frequency"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gtk_stack_set_visible_child(data->stack, data->sound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open Create/Modify Event Dialog
|
||||||
|
void open_dialog(GtkWidget *widget, struct event_location *location, int free_location) {
|
||||||
|
GtkBuilder *builder = gtk_builder_new();
|
||||||
|
|
||||||
|
GError *err = NULL;
|
||||||
|
if (gtk_builder_add_from_resource(builder, "/dialog_window.glade", &err) == 0) {
|
||||||
|
g_error("Error Loading UI: %s", err->message);
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkWindow *window = GTK_WINDOW(gtk_builder_get_object(builder, "dialog_window"));
|
||||||
|
|
||||||
|
gtk_builder_connect_signals(builder, NULL);
|
||||||
|
|
||||||
|
struct save_event_data *data = malloc(sizeof (struct save_event_data));
|
||||||
|
data->location = location;
|
||||||
|
data->window = window;
|
||||||
|
data->name = GTK_ENTRY(gtk_builder_get_object(builder, "name"));
|
||||||
|
data->stack = GTK_STACK(gtk_builder_get_object(builder, "stack"));
|
||||||
|
// Sound
|
||||||
|
data->sound = GTK_WIDGET(gtk_builder_get_object(builder, "sound"));
|
||||||
|
data->sound_name = GTK_ENTRY(gtk_builder_get_object(builder, "sound-name"));
|
||||||
|
// Vibration (Rumble)
|
||||||
|
data->vibration_rumble = GTK_WIDGET(gtk_builder_get_object(builder, "vibration-rumble"));
|
||||||
|
data->vibration_rumble_duration = GTK_ENTRY(gtk_builder_get_object(builder, "vibration-rumble-duration"));
|
||||||
|
// Vibration (Periodic)
|
||||||
|
data->vibration_periodic = GTK_WIDGET(gtk_builder_get_object(builder, "vibration-periodic"));
|
||||||
|
data->magnitude = GTK_ENTRY(gtk_builder_get_object(builder, "magnitude"));
|
||||||
|
data->vibration_periodic_duration = GTK_ENTRY(gtk_builder_get_object(builder, "vibration-periodic-duration"));
|
||||||
|
data->fade_in_time = GTK_ENTRY(gtk_builder_get_object(builder, "fade-in-time"));
|
||||||
|
data->fade_in_level = GTK_ENTRY(gtk_builder_get_object(builder, "fade-in-level"));
|
||||||
|
// LED
|
||||||
|
data->led = GTK_WIDGET(gtk_builder_get_object(builder, "led"));
|
||||||
|
data->color = GTK_ENTRY(gtk_builder_get_object(builder, "color"));
|
||||||
|
data->frequency = GTK_ENTRY(gtk_builder_get_object(builder, "frequency"));
|
||||||
|
|
||||||
|
g_signal_connect(gtk_builder_get_object(builder, "save"), "clicked", G_CALLBACK(on_save_event), data);
|
||||||
|
|
||||||
|
g_signal_connect(window, "destroy", G_CALLBACK(free_userdata), data);
|
||||||
|
if (free_location) {
|
||||||
|
g_signal_connect(window, "destroy", G_CALLBACK(free_userdata), location);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_unref(builder);
|
||||||
|
|
||||||
|
apply_event_to_dialog(data);
|
||||||
|
|
||||||
|
GtkWindow *parent_window = GTK_WINDOW(gtk_widget_get_toplevel(widget));
|
||||||
|
|
||||||
|
gtk_window_set_application(window, gtk_window_get_application(parent_window));
|
||||||
|
|
||||||
|
gtk_window_set_modal(window, 1);
|
||||||
|
gtk_window_set_transient_for(window, parent_window);
|
||||||
|
|
||||||
|
gtk_window_present(window);
|
||||||
|
}
|
11
src/dialog.h
Normal file
11
src/dialog.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#ifndef DIALOG_H
|
||||||
|
|
||||||
|
#define DIALOG_H
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
void open_dialog(GtkWidget *, struct event_location *, int);
|
||||||
|
|
||||||
|
#endif
|
475
src/dialog_window.glade
Normal file
475
src/dialog_window.glade
Normal file
@ -0,0 +1,475 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Generated with glade 3.22.2 -->
|
||||||
|
<interface>
|
||||||
|
<requires lib="gtk+" version="3.20"/>
|
||||||
|
<requires lib="libhandy" version="1.0"/>
|
||||||
|
<object class="HdyWindow" id="dialog_window">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="window_position">center-on-parent</property>
|
||||||
|
<property name="type_hint">dialog</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<object class="HdyHeaderBar">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="valign">start</property>
|
||||||
|
<property name="title" translatable="yes">Create/Modify Event</property>
|
||||||
|
<property name="show_close_button">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="save">
|
||||||
|
<property name="label" translatable="yes">Save</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="pack_type">end</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="margin_left">16</property>
|
||||||
|
<property name="margin_right">16</property>
|
||||||
|
<property name="margin_top">16</property>
|
||||||
|
<property name="margin_bottom">16</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Name:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="name">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="HdyViewSwitcher">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="narrow_ellipsize">end</property>
|
||||||
|
<property name="stack">stack</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkStack" id="stack">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="transition_type">crossfade</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="sound">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="valign">start</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Sound Name:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="sound-name">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="title" translatable="yes">Sound</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="vibration-rumble">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="valign">start</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Duration:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="vibration-rumble-duration">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="input_purpose">digits</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="title" translatable="yes">Vibration (Rumble)</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="vibration-periodic">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="valign">start</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="valign">start</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Magnitude:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="magnitude">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="input_purpose">digits</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="valign">start</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Duration:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="vibration-periodic-duration">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="input_purpose">digits</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="valign">start</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Fade In Time:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="fade-in-time">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="input_purpose">digits</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="valign">start</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Fade In Level:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="fade-in-level">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="input_purpose">digits</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="title" translatable="yes">Vibration (Periodic)</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="led">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="valign">start</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Color:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="color">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Frequency:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="frequency">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="input_purpose">digits</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">4</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="title" translatable="yes">LED</property>
|
||||||
|
<property name="position">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</interface>
|
278
src/main.c
Normal file
278
src/main.c
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <json-glib/json-glib.h>
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
#include <handy.h>
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
|
#include "dialog.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
static void on_startup() {
|
||||||
|
hdy_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CONFIG_JSON "/usr/share/feedbackd/themes/default.json"
|
||||||
|
#define ADMIN_CONFIG_JSON "admin://"CONFIG_JSON
|
||||||
|
|
||||||
|
JsonNode *root = NULL;
|
||||||
|
|
||||||
|
// Extracts Profile Obj From JSON Root (Only Runs In Init)
|
||||||
|
static JsonArray *get_profile(gchar *name) {
|
||||||
|
if (root != NULL) {
|
||||||
|
JsonObject *obj = json_node_get_object(root);
|
||||||
|
if (obj != NULL) {
|
||||||
|
JsonArray *profiles = json_object_get_array_member(obj, "profiles");
|
||||||
|
if (profiles != NULL) {
|
||||||
|
int length = json_array_get_length(profiles);
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
JsonObject *element = json_array_get_object_element(profiles, i);
|
||||||
|
if (element != NULL) {
|
||||||
|
const gchar *elementName = json_object_get_string_member(element, "name");
|
||||||
|
if (g_strcmp0(name, elementName) == 0) {
|
||||||
|
return json_object_get_array_member(element, "feedbacks");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LABEL_MARGIN 16
|
||||||
|
|
||||||
|
// Event Delete Handler
|
||||||
|
static void on_delete(__attribute__((unused)) GtkButton *button, struct event_location *data) {
|
||||||
|
json_array_remove_element(data->profile, data->id);
|
||||||
|
reload_profiles(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler For Freeing Userdata
|
||||||
|
void free_userdata(__attribute__((unused)) GtkWidget *widget, void *userdata) {
|
||||||
|
free(userdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Edit Event Row Handler
|
||||||
|
static void on_edit(GtkWidget *button, struct event_location *data) {
|
||||||
|
open_dialog(button, data, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load Profile Into List
|
||||||
|
static void load_profile(GtkListBox *list) {
|
||||||
|
char *name = profile_list_to_name(list);
|
||||||
|
|
||||||
|
gtk_container_foreach(GTK_CONTAINER(list), (GtkCallback) gtk_widget_destroy, NULL);
|
||||||
|
|
||||||
|
JsonArray *profile = name_to_profile(name);
|
||||||
|
if (profile != NULL && list != NULL) {
|
||||||
|
int length = json_array_get_length(profile);
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
JsonObject *event = json_array_get_object_element(profile, i);
|
||||||
|
if (event != NULL) {
|
||||||
|
const gchar *event_name = json_object_get_string_member(event, "event-name");
|
||||||
|
if (event_name != NULL) {
|
||||||
|
GtkWidget *row = gtk_list_box_row_new();
|
||||||
|
gtk_widget_set_visible(row, 1);
|
||||||
|
|
||||||
|
GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
|
||||||
|
gtk_widget_set_visible(box, 1);
|
||||||
|
|
||||||
|
gtk_widget_set_halign(box, GTK_ALIGN_FILL);
|
||||||
|
|
||||||
|
GtkWidget *label = gtk_label_new(event_name);
|
||||||
|
gtk_widget_set_visible(label, 1);
|
||||||
|
|
||||||
|
gtk_label_set_xalign(GTK_LABEL(label), 0);
|
||||||
|
gtk_label_set_lines(GTK_LABEL(label), 2);
|
||||||
|
gtk_label_set_line_wrap(GTK_LABEL(label), 1);
|
||||||
|
gtk_label_set_line_wrap_mode(GTK_LABEL(label), PANGO_WRAP_WORD_CHAR);
|
||||||
|
gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END);
|
||||||
|
|
||||||
|
gtk_widget_set_margin_start(label, LABEL_MARGIN);
|
||||||
|
gtk_widget_set_margin_end(label, LABEL_MARGIN);
|
||||||
|
gtk_widget_set_margin_top(label, LABEL_MARGIN);
|
||||||
|
gtk_widget_set_margin_bottom(label, LABEL_MARGIN);
|
||||||
|
|
||||||
|
gtk_box_pack_start(GTK_BOX(box), label, 0, 0, 0);
|
||||||
|
|
||||||
|
GtkWidget *edit_button = gtk_button_new_with_label("Edit");
|
||||||
|
gtk_widget_set_visible(edit_button, 1);
|
||||||
|
|
||||||
|
gtk_widget_set_halign(edit_button, GTK_ALIGN_END);
|
||||||
|
gtk_widget_set_valign(edit_button, GTK_ALIGN_CENTER);
|
||||||
|
|
||||||
|
struct event_location *location = malloc(sizeof (struct event_location));
|
||||||
|
location->profile = profile;
|
||||||
|
location->id = i;
|
||||||
|
|
||||||
|
g_signal_connect(edit_button, "clicked", G_CALLBACK(on_edit), location);
|
||||||
|
|
||||||
|
gtk_widget_set_margin_end(edit_button, LABEL_MARGIN);
|
||||||
|
gtk_widget_set_margin_top(edit_button, LABEL_MARGIN);
|
||||||
|
gtk_widget_set_margin_bottom(edit_button, LABEL_MARGIN);
|
||||||
|
|
||||||
|
GtkWidget *delete_button = gtk_button_new_with_label("Delete");
|
||||||
|
gtk_widget_set_visible(delete_button, 1);
|
||||||
|
|
||||||
|
gtk_widget_set_halign(delete_button, GTK_ALIGN_END);
|
||||||
|
gtk_widget_set_valign(delete_button, GTK_ALIGN_CENTER);
|
||||||
|
|
||||||
|
g_signal_connect(delete_button, "clicked", G_CALLBACK(on_delete), location);
|
||||||
|
|
||||||
|
gtk_widget_set_margin_end(delete_button, LABEL_MARGIN);
|
||||||
|
gtk_widget_set_margin_top(delete_button, LABEL_MARGIN);
|
||||||
|
gtk_widget_set_margin_bottom(delete_button, LABEL_MARGIN);
|
||||||
|
|
||||||
|
gtk_box_pack_end(GTK_BOX(box), delete_button, 0, 0, 0);
|
||||||
|
gtk_box_pack_end(GTK_BOX(box), edit_button, 0, 0, 0);
|
||||||
|
|
||||||
|
gtk_container_add(GTK_CONTAINER(row), box);
|
||||||
|
|
||||||
|
g_signal_connect(row, "destroy", G_CALLBACK(free_userdata), location);
|
||||||
|
|
||||||
|
gtk_container_add(GTK_CONTAINER(list), row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON File On admin://
|
||||||
|
GFile *file;
|
||||||
|
|
||||||
|
// Reload And Save Profiles
|
||||||
|
void reload_profiles(int save) {
|
||||||
|
load_profile(get_app_data()->full_list);
|
||||||
|
load_profile(get_app_data()->quiet_list);
|
||||||
|
load_profile(get_app_data()->silent_list);
|
||||||
|
|
||||||
|
if (save) {
|
||||||
|
JsonGenerator *generator = json_generator_new();
|
||||||
|
|
||||||
|
json_generator_set_root(generator, root);
|
||||||
|
json_generator_set_pretty(generator, 1);
|
||||||
|
json_generator_set_indent(generator, 4);
|
||||||
|
json_generator_set_indent_char(generator, ' ');
|
||||||
|
|
||||||
|
GError *open_err = NULL;
|
||||||
|
GFileOutputStream *stream = g_file_replace(file, NULL, 0, G_FILE_CREATE_NONE, NULL, &open_err);
|
||||||
|
if (stream == NULL) {
|
||||||
|
g_error("Error Opening File Stream: %s", open_err->message);
|
||||||
|
}
|
||||||
|
|
||||||
|
GError *write_err = NULL;
|
||||||
|
if (json_generator_to_stream(generator, G_OUTPUT_STREAM(stream), NULL, &write_err) == 0) {
|
||||||
|
g_error("Error Saving JSON: %s", write_err->message);
|
||||||
|
}
|
||||||
|
|
||||||
|
GError *close_err = NULL;
|
||||||
|
if (g_output_stream_close(G_OUTPUT_STREAM(stream), NULL, &close_err) == 0) {
|
||||||
|
g_error("Error Closing File Stream: %s", close_err->message);
|
||||||
|
}
|
||||||
|
g_object_unref(stream);
|
||||||
|
|
||||||
|
// Restart FeedbackD
|
||||||
|
system("killall feedbackd");
|
||||||
|
|
||||||
|
g_object_unref(generator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New Event Handler
|
||||||
|
void on_new_event(GtkButton *button, GtkStack *stack) {
|
||||||
|
struct event_location *location = malloc(sizeof (struct event_location));
|
||||||
|
location->id = -1;
|
||||||
|
GtkListBox *visible_widget = GTK_LIST_BOX(gtk_stack_get_visible_child(stack));
|
||||||
|
location->profile = name_to_profile(profile_list_to_name(visible_widget));
|
||||||
|
open_dialog(GTK_WIDGET(button), location, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finish Mount Handler
|
||||||
|
static void finish_mount(__attribute__((unused)) GObject *object, GAsyncResult *result, __attribute__((unused)) gpointer user_data) {
|
||||||
|
GError *err = NULL;
|
||||||
|
if (g_file_mount_enclosing_volume_finish(file, result, &err) == 0) {
|
||||||
|
g_error("Error Mounting JSON: %s", err->message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// App Start Handler
|
||||||
|
static void on_activate(GtkApplication *app) {
|
||||||
|
g_assert(GTK_IS_APPLICATION(app));
|
||||||
|
|
||||||
|
GtkWindow *window = gtk_application_get_active_window(app);
|
||||||
|
|
||||||
|
if (window == NULL) {
|
||||||
|
GtkBuilder *builder = gtk_builder_new();
|
||||||
|
|
||||||
|
GError *err = NULL;
|
||||||
|
if (gtk_builder_add_from_resource(builder, "/main_window.glade", &err) == 0) {
|
||||||
|
g_error("Error Loading UI: %s", err->message);
|
||||||
|
}
|
||||||
|
|
||||||
|
window = GTK_WINDOW(gtk_builder_get_object(builder, "main_window"));
|
||||||
|
|
||||||
|
gtk_builder_connect_signals(builder, NULL);
|
||||||
|
|
||||||
|
JsonParser *parser = json_parser_new();
|
||||||
|
|
||||||
|
GError *json_err = NULL;
|
||||||
|
if (json_parser_load_from_file(parser, CONFIG_JSON, &json_err) == 0) {
|
||||||
|
g_error("Error Parsing JSON: %s", json_err->message);
|
||||||
|
}
|
||||||
|
|
||||||
|
root = json_node_copy(json_parser_get_root(parser));
|
||||||
|
|
||||||
|
init_app_data();
|
||||||
|
|
||||||
|
get_app_data()->full_list = GTK_LIST_BOX(gtk_builder_get_object(builder, "full"));
|
||||||
|
get_app_data()->full = get_profile("full");
|
||||||
|
get_app_data()->quiet_list = GTK_LIST_BOX(gtk_builder_get_object(builder, "quiet"));
|
||||||
|
get_app_data()->quiet = get_profile("quiet");
|
||||||
|
get_app_data()->silent_list = GTK_LIST_BOX(gtk_builder_get_object(builder, "silent"));
|
||||||
|
get_app_data()->silent = get_profile("silent");
|
||||||
|
|
||||||
|
reload_profiles(0);
|
||||||
|
|
||||||
|
g_object_unref(parser);
|
||||||
|
|
||||||
|
g_object_unref(builder);
|
||||||
|
|
||||||
|
gtk_window_set_application(window, app);
|
||||||
|
|
||||||
|
file = g_file_new_for_uri(ADMIN_CONFIG_JSON);
|
||||||
|
|
||||||
|
// Mount File
|
||||||
|
GMountOperation *operation = gtk_mount_operation_new(window);
|
||||||
|
g_file_mount_enclosing_volume(file, G_MOUNT_MOUNT_NONE, operation, NULL, finish_mount, NULL);
|
||||||
|
g_object_unref(operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_window_present(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Memory Cleanup
|
||||||
|
static void cleanup() {
|
||||||
|
if (root != NULL) {
|
||||||
|
json_node_free(root);
|
||||||
|
}
|
||||||
|
free_app_data();
|
||||||
|
if (file != NULL) {
|
||||||
|
g_object_unref(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
GtkApplication *app = gtk_application_new("com.thebrokenrail.FeedbackD-Configuration", G_APPLICATION_FLAGS_NONE);
|
||||||
|
|
||||||
|
g_signal_connect(app, "startup", G_CALLBACK(on_startup), NULL);
|
||||||
|
g_signal_connect(app, "activate", G_CALLBACK(on_activate), NULL);
|
||||||
|
|
||||||
|
int ret = g_application_run(G_APPLICATION(app), argc, argv);
|
||||||
|
|
||||||
|
cleanup();
|
||||||
|
|
||||||
|
g_object_unref(app);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
17
src/main.h
Normal file
17
src/main.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef MAIN_H
|
||||||
|
|
||||||
|
#define MAIN_H
|
||||||
|
|
||||||
|
#include <json-glib/json-glib.h>
|
||||||
|
|
||||||
|
// Represnts One Item In A Profile
|
||||||
|
struct event_location {
|
||||||
|
JsonArray *profile;
|
||||||
|
int id;
|
||||||
|
};
|
||||||
|
|
||||||
|
void free_userdata(GtkWidget *, void *);
|
||||||
|
|
||||||
|
void reload_profiles(int);
|
||||||
|
|
||||||
|
#endif
|
136
src/main_window.glade
Normal file
136
src/main_window.glade
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Generated with glade 3.22.2 -->
|
||||||
|
<interface>
|
||||||
|
<requires lib="gtk+" version="3.20"/>
|
||||||
|
<requires lib="libhandy" version="1.0"/>
|
||||||
|
<object class="HdyApplicationWindow" id="main_window">
|
||||||
|
<property name="width_request">360</property>
|
||||||
|
<property name="height_request">360</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<object class="HdyHeaderBar">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="valign">start</property>
|
||||||
|
<property name="title" translatable="yes">FeedbackD Configuration</property>
|
||||||
|
<property name="show_close_button">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton">
|
||||||
|
<property name="label" translatable="yes">New</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<signal name="clicked" handler="on_new_event" object="stack" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="pack_type">end</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScrolledWindow">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="hscrollbar_policy">never</property>
|
||||||
|
<property name="shadow_type">in</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkViewport">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScrolledWindow">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="margin_left">32</property>
|
||||||
|
<property name="margin_right">32</property>
|
||||||
|
<property name="margin_top">32</property>
|
||||||
|
<property name="margin_bottom">32</property>
|
||||||
|
<property name="vexpand">True</property>
|
||||||
|
<property name="hscrollbar_policy">never</property>
|
||||||
|
<property name="vscrollbar_policy">never</property>
|
||||||
|
<property name="shadow_type">in</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkViewport">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkStack" id="stack">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="transition_type">crossfade</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkListBox" id="full">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="selection_mode">none</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="title" translatable="yes">Full</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkListBox" id="quiet">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="selection_mode">none</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="title" translatable="yes">Quiet</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkListBox" id="silent">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="selection_mode">none</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="title" translatable="yes">Silent</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="HdyViewSwitcherBar">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="policy">auto</property>
|
||||||
|
<property name="stack">stack</property>
|
||||||
|
<property name="reveal">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</interface>
|
46
src/util.c
Normal file
46
src/util.c
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
struct app_data *app_data = NULL;
|
||||||
|
|
||||||
|
// Initialize App Data
|
||||||
|
void init_app_data() {
|
||||||
|
app_data = malloc(sizeof (struct app_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get App Data
|
||||||
|
struct app_data *get_app_data() {
|
||||||
|
return app_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free App Data
|
||||||
|
void free_app_data() {
|
||||||
|
if (app_data != NULL) {
|
||||||
|
free(app_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert Profile List Widget To Profile Name
|
||||||
|
char *profile_list_to_name(GtkListBox *profile) {
|
||||||
|
if (profile == app_data->full_list) {
|
||||||
|
return "full";
|
||||||
|
} else if (profile == app_data->quiet_list) {
|
||||||
|
return "quiet";
|
||||||
|
} else if (profile == app_data->silent_list) {
|
||||||
|
return "silent";
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert Name To Profile Obj
|
||||||
|
JsonArray *name_to_profile(char *name) {
|
||||||
|
if (g_strcmp0(name, "full") == 0) {
|
||||||
|
return app_data->full;
|
||||||
|
} else if (g_strcmp0(name, "quiet") == 0) {
|
||||||
|
return app_data->quiet;
|
||||||
|
} else if (g_strcmp0(name, "silent") == 0) {
|
||||||
|
return app_data->silent;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
25
src/util.h
Normal file
25
src/util.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef UTIL_H
|
||||||
|
|
||||||
|
#define UTIL_H
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
#include <json-glib/json-glib.h>
|
||||||
|
|
||||||
|
// Global App Context
|
||||||
|
struct app_data {
|
||||||
|
GtkListBox *full_list;
|
||||||
|
JsonArray *full;
|
||||||
|
GtkListBox *quiet_list;
|
||||||
|
JsonArray *quiet;
|
||||||
|
GtkListBox *silent_list;
|
||||||
|
JsonArray *silent;
|
||||||
|
};
|
||||||
|
|
||||||
|
void init_app_data();
|
||||||
|
struct app_data *get_app_data();
|
||||||
|
void free_app_data();
|
||||||
|
|
||||||
|
char *profile_list_to_name(GtkListBox *);
|
||||||
|
JsonArray *name_to_profile(char *);
|
||||||
|
|
||||||
|
#endif
|
Reference in New Issue
Block a user