diff --git a/base/regsvc/CMakeLists.txt b/base/regsvc/CMakeLists.txt new file mode 100644 index 0000000..d9ebfdd --- /dev/null +++ b/base/regsvc/CMakeLists.txt @@ -0,0 +1,82 @@ +cmake_minimum_required(VERSION 3.5) + +project( + wintc-regsvc + VERSION 1.0 + DESCRIPTION "Windows Total Conversion registry service." + LANGUAGES C +) + +set(PROJECT_ANYARCH false) +set(PROJECT_FREESTATUS true) +set(PROJECT_MAINTAINER "Rory Fewell ") + +set(PROJECT_ROOT ${CMAKE_CURRENT_LIST_DIR}) + +include(GNUInstallDirs) + +include(../../packaging/cmake-inc/common/CMakeLists.txt) +include(../../packaging/cmake-inc/linking/CMakeLists.txt) +include(../../packaging/cmake-inc/packaging/CMakeLists.txt) + +wintc_resolve_library(glib-2.0 GLIB) +wintc_resolve_library(gtk+-3.0 GTK3) +wintc_resolve_library(sqlite3 SQLITE3) +wintc_resolve_library(wintc-comgtk WINTC_COMGTK) +wintc_resolve_library(wintc-registry WINTC_REGISTRY) + +add_executable( + wintc-regsvc + src/backend.c + src/backend.h + src/main.c +) + +set_target_properties( + wintc-regsvc + PROPERTIES + OUTPUT_NAME regsvc +) + +target_compile_options( + wintc-regsvc + PRIVATE ${WINTC_COMPILE_OPTIONS} +) + +target_include_directories( + wintc-regsvc + SYSTEM + BEFORE + PRIVATE ${GLIB_INCLUDE_DIRS} + PRIVATE ${GTK3_INCLUDE_DIRS} + PRIVATE ${SQLITE3_INCLUDE_DIRS} + PRIVATE ${WINTC_COMGTK_INCLUDE_DIRS} + PRIVATE ${WINTC_REGISTRY_INCLUDE_DIRS} +) + +target_link_directories( + wintc-regsvc + PRIVATE ${GLIB_LIBRARY_DIRS} + PRIVATE ${GTK3_LIBRARY_DIRS} + PRIVATE ${SQLITE3_LIBRARY_DIRS} + PRIVATE ${WINTC_COMGTK_LIBRARY_DIRS} + PRIVATE ${WINTC_REGISTRY_LIBRARY_DIRS} +) + +target_link_libraries( + wintc-regsvc + PRIVATE ${GLIB_LIBRARIES} + PRIVATE ${GTK3_LIBRARIES} + PRIVATE ${SQLITE3_LIBRARIES} + PRIVATE ${WINTC_COMGTK_LIBRARIES} + PRIVATE ${WINTC_REGISTRY_LIBRARIES} +) + +# Installation +# +wintc_configure_and_install_packaging() + +install( + TARGETS wintc-regsvc + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) diff --git a/base/regsvc/README.MD b/base/regsvc/README.MD new file mode 100644 index 0000000..046db6f --- /dev/null +++ b/base/regsvc/README.MD @@ -0,0 +1,8 @@ +# regsvc +This directory contains the source-code for the registry service/daemon. + +## Details +Essentially `regsvc` is a very billy-basic daemon that provides the backend for the simplified 'registry' interface defined by `libwintc-registry`. +- The registry is implemented as a daemon for DBus-based IPC, so things like Control Panel applets can configure settings and the recipient programs (like the desktop) can be notified +- It's not a full clone of the Windows Registry, just a very simple form +- The backend uses SQLite3 for storage in the user profile diff --git a/base/regsvc/deps b/base/regsvc/deps new file mode 100644 index 0000000..c95759c --- /dev/null +++ b/base/regsvc/deps @@ -0,0 +1,5 @@ +bt,rt:glib2 +bt,rt:gtk3 +bt,rt:sqlite3 +bt,rt:wintc-comgtk +bt,rt:wintc-registry diff --git a/base/regsvc/src/backend.c b/base/regsvc/src/backend.c new file mode 100644 index 0000000..38e8ca7 --- /dev/null +++ b/base/regsvc/src/backend.c @@ -0,0 +1,713 @@ +#include +#include +#include + +#include "backend.h" + +#define WINTC_COMPONENT_REGISTRY "registry" + +#define BAIL_ON_FAIL(query) \ + rc = (query); \ + if (rc == SQLITE_ERROR || rc == SQLITE_MISUSE) \ + { \ + handle_sqlite_problem(rc); \ + sqlite3_finalize(stmt); \ + return FALSE; \ + } + +// +// PRIVATE CONSTANTS +// +static const gchar* S_SQL_INIT = +"CREATE TABLE IF NOT EXISTS RegKey (" +" Id INTEGER PRIMARY KEY," +" ParentId INTEGER," +" Name VARCHAR(255) NOT NULL," +" FOREIGN KEY(ParentId) REFERENCES RegKey(Id)," +" UNIQUE(ParentId, Name)" +");" +"CREATE TABLE IF NOT EXISTS RegKeyValue (" +" Id INTEGER PRIMARY KEY," +" OwnerKey INTEGER," +" Name VARCHAR(255)," +" ItemType INTEGER," +" ItemValue BLOB," +" FOREIGN KEY(OwnerKey) REFERENCES RegKey(Id)," +" UNIQUE(OwnerKey, Name)" +");" +"INSERT OR REPLACE INTO RegKey VALUES (1, NULL, 'HKEY_CURRENT_USER');"; + +// +// STATIC DATA +// +static sqlite3* s_db = NULL; + +// +// FORWARD DECLARATIONS +// +static gboolean db_create_regkey( + const gchar* key_name, + gint parent_id, + gint* out_id +); +static gboolean db_get_regkey_id( + const gchar* key_name, + gint parent_id, + gint* out_id +); +static gboolean db_traverse_to_key( + const gchar* key_path, + gint* out_id +); + +static void handle_sqlite_problem( + gint rc +); + +static gboolean is_valid_key_path( + GSList* components +); + +static gboolean is_valid_value_name( + const gchar* value_name +); + +static GSList* path_to_components( + const gchar* key_path +); + +// +// INTERNAL FUNCTIONS +// +void backend_close(void) +{ + if (!s_db) + { + return; + } + + WINTC_LOG_DEBUG("%s", "regsvc: sqlite closing profile"); + + sqlite3_close(s_db); + s_db = NULL; +} + +gboolean backend_init(void) +{ + GError* error = NULL; + gchar* path; + gint rc; + + if (s_db) + { + return TRUE; + } + + // Open DB handle + // + if (!wintc_profile_ensure_exists(WINTC_COMPONENT_REGISTRY, &error)) + { + wintc_log_error_and_clear(&error); + return FALSE; + } + + path = wintc_profile_get_path(WINTC_COMPONENT_REGISTRY, "ntuser.db"); + rc = sqlite3_open(path, &s_db); + + WINTC_LOG_DEBUG("regsvc: sqlite opening %s", path); + + g_free(path); + + if (rc) + { + g_critical( + "regsvc: failed to open profile sqlite %s", + sqlite3_errmsg(s_db) + ); + sqlite3_close(s_db); + return FALSE; + } + + // Ensure tables initialised + // + char* error_sql = NULL; + + rc = + sqlite3_exec( + s_db, + S_SQL_INIT, + NULL, + NULL, + &error_sql + ); + + if (rc != SQLITE_OK) + { + g_critical( + "regsvc: failed to init tables %s", + error_sql + ); + + sqlite3_free(error_sql); + + return FALSE; + } + + return TRUE; +} + +gboolean backend_create_key( + const gchar* key_path +) +{ + GSList* components = path_to_components(key_path); + + WINTC_LOG_DEBUG("regsvc: backend create key %s", key_path); + + if (!is_valid_key_path(components)) + { + WINTC_LOG_DEBUG( + "regsvc: backend create key: key/value not valid '%s'", + key_path + ); + + g_slist_free_full(components, g_free); + + return FALSE; + } + + // Iterate over to create the key + // + gint last_id = 0; + gboolean success = TRUE; + + for (GSList* iter = components; iter; iter = iter->next) + { + const gchar* key_name = (gchar*) iter->data; + gint this_id = 0; + + WINTC_LOG_DEBUG("regsvc: create key checking for part '%s'", key_name); + + // Check if key exists first + // + if (!db_get_regkey_id(key_name, last_id, &this_id)) + { + success = FALSE; + break; + } + + // If needed, create the key + // + if (!this_id) + { + WINTC_LOG_DEBUG("%s", "regsvc: need to create that key"); + + if (!db_create_regkey(key_name, last_id, &this_id)) + { + success = FALSE; + break; + } + } + + last_id = this_id; + } + + g_slist_free_full(components, g_free); + + return success; +} + +gboolean backend_get_key_value( + const gchar* key_path, + const gchar* value_name, + WinTCRegistryValueType value_type, + void* value_data +) +{ + gint key_id = 0; + gint rc; + + if (value_data == NULL) + { + g_critical("%s", "regsvc: backend nowhere to store kv"); + return FALSE; + } + + if ( + !is_valid_value_name(value_name) || + !db_traverse_to_key(key_path, &key_id) + ) + { + WINTC_LOG_DEBUG( + "regsvc: backend get kv: key/value not valid '%s->%s'", + key_path, + value_name + ); + return FALSE; + } + + // Retrieve key stuff + // + sqlite3_stmt* stmt = NULL; + + BAIL_ON_FAIL( + sqlite3_prepare( + s_db, + "SELECT ItemType, ItemValue FROM RegKeyValue WHERE OwnerKey = ? AND Name = ?;", + -1, + &stmt, + NULL + ) + ); + + BAIL_ON_FAIL(sqlite3_bind_int(stmt, 1, key_id)); + BAIL_ON_FAIL(sqlite3_bind_text(stmt, 2, value_name, -1, SQLITE_STATIC)); + + BAIL_ON_FAIL(sqlite3_step(stmt)); + + // Analyze result + // + WinTCRegistryValueType found_type = WINTC_REG_INVALID; + gboolean success = FALSE; + + if (rc == SQLITE_ROW) + { + found_type = (WinTCRegistryValueType) sqlite3_column_int(stmt, 0); + + if (found_type == value_type) + { + switch (value_type) + { + case WINTC_REG_DWORD: + *((gint*) value_data) = + sqlite3_column_int(stmt, 1); + break; + + case WINTC_REG_QWORD: + *((gint64*) value_data) = + sqlite3_column_int64(stmt, 1); + break; + + case WINTC_REG_SZ: + *((gchar**) value_data) = + g_strdup((const gchar*) sqlite3_column_text(stmt, 1)); + break; + + default: + g_critical( + "regsvc: backend unknown key type %d", + value_type + ); + break; + } + + success = TRUE; + } + } + + sqlite3_finalize(stmt); + + return success; +} + +gboolean backend_set_key_value( + const gchar* key_path, + const gchar* value_name, + WinTCRegistryValueType value_type, + void* value_data +) +{ + gint key_id = 0; + gint rc; + + if (value_data == NULL) + { + g_critical("%s", "regsvc: backend no data provided to set kv"); + return FALSE; + } + + if ( + !is_valid_value_name(value_name) || + !db_traverse_to_key(key_path, &key_id) + ) + { + WINTC_LOG_DEBUG( + "regsvc: backend set kv: key/value not valid '%s->%s'", + key_path, + value_name + ); + return FALSE; + } + + WINTC_LOG_DEBUG("regsvc: traversed to key id %d", key_id); + + // Check if we already have a key value + // + gint value_id = 0; + sqlite3_stmt* stmt = NULL; + + BAIL_ON_FAIL( + sqlite3_prepare( + s_db, + "SELECT Id FROM RegKeyValue WHERE OwnerKey = ? AND Name = ?;", + -1, + &stmt, + NULL + ) + ); + + BAIL_ON_FAIL(sqlite3_bind_int(stmt, 1, key_id)); + BAIL_ON_FAIL(sqlite3_bind_text(stmt, 2, value_name, -1, SQLITE_STATIC)); + + BAIL_ON_FAIL(sqlite3_step(stmt)); + + if (rc == SQLITE_ROW) + { + value_id = sqlite3_column_int(stmt, 0); + } + + sqlite3_finalize(stmt); + + // Set new value + // + gint blob_bind_pos; + + if (value_id) + { + BAIL_ON_FAIL( + sqlite3_prepare( + s_db, + "UPDATE RegKeyValue SET ItemType = ?, ItemValue = ? WHERE Id = ?;", + -1, + &stmt, + NULL + ) + ); + + BAIL_ON_FAIL(sqlite3_bind_int(stmt, 1, value_type)); + BAIL_ON_FAIL(sqlite3_bind_int(stmt, 3, value_id)); + + blob_bind_pos = 2; + } + else + { + BAIL_ON_FAIL( + sqlite3_prepare( + s_db, + "INSERT INTO RegKeyValue VALUES (NULL, ?, ?, ?, ?);", + -1, + &stmt, + NULL + ) + ); + + BAIL_ON_FAIL(sqlite3_bind_int(stmt, 1, key_id)); + BAIL_ON_FAIL( + sqlite3_bind_text( + stmt, + 2, + value_name, + -1, + SQLITE_STATIC + ) + ); + BAIL_ON_FAIL(sqlite3_bind_int(stmt, 3, value_type)); + + blob_bind_pos = 4; + } + + switch (value_type) + { + case WINTC_REG_DWORD: + BAIL_ON_FAIL( + sqlite3_bind_int( + stmt, + blob_bind_pos, + *((gint*) value_data) + ) + ); + break; + + case WINTC_REG_QWORD: + BAIL_ON_FAIL( + sqlite3_bind_int64( + stmt, + blob_bind_pos, + *((gint64*) value_data) + ) + ); + break; + + case WINTC_REG_SZ: + BAIL_ON_FAIL( + sqlite3_bind_text( + stmt, + blob_bind_pos, + *((gchar**) value_data), + -1, + SQLITE_STATIC + ) + ); + break; + + default: + g_critical("regsvc: sqlite no binding for %d", value_type); + break; + } + + BAIL_ON_FAIL(sqlite3_step(stmt)); + + sqlite3_finalize(stmt); + + return TRUE; +} + +// +// PRIVATE FUNCTIONS +// +static gboolean db_create_regkey( + const gchar* key_name, + gint parent_id, + gint* out_id +) +{ + gint rc; + sqlite3_stmt* stmt = NULL; + + WINTC_SAFE_REF_SET(out_id, 0); + + BAIL_ON_FAIL( + sqlite3_prepare( + s_db, + "INSERT INTO RegKey VALUES (NULL, ?, ?);", + -1, + &stmt, + NULL + ) + ); + + if (!parent_id) + { + BAIL_ON_FAIL(sqlite3_bind_null(stmt, 1)); + } + else + { + BAIL_ON_FAIL(sqlite3_bind_int(stmt, 1, parent_id)); + } + + BAIL_ON_FAIL( + sqlite3_bind_text( + stmt, + 2, + key_name, + -1, + SQLITE_STATIC + ) + ); + + BAIL_ON_FAIL(sqlite3_step(stmt)); + + return db_get_regkey_id( + key_name, + parent_id, + out_id + ); +} + +static gboolean db_get_regkey_id( + const gchar* key_name, + gint parent_id, + gint* out_id +) +{ + gint rc; + sqlite3_stmt* stmt = NULL; + + WINTC_SAFE_REF_SET(out_id, 0); + + BAIL_ON_FAIL( + sqlite3_prepare( + s_db, + "SELECT Id FROM RegKey WHERE ParentId IS ? AND Name = ?;", + -1, + &stmt, + NULL + ) + ); + + if (!parent_id) + { + BAIL_ON_FAIL(sqlite3_bind_null(stmt, 1)); + } + else + { + BAIL_ON_FAIL(sqlite3_bind_int(stmt, 1, parent_id)); + } + + BAIL_ON_FAIL(sqlite3_bind_text(stmt, 2, key_name, -1, SQLITE_STATIC)); + + BAIL_ON_FAIL(sqlite3_step(stmt)); + + if (rc == SQLITE_ROW) + { + WINTC_SAFE_REF_SET(out_id, sqlite3_column_int(stmt, 0)); + } + + sqlite3_finalize(stmt); + return TRUE; +} + +static gboolean db_traverse_to_key( + const gchar* key_path, + gint* out_id +) +{ + GSList* components = path_to_components(key_path); + + WINTC_SAFE_REF_SET(out_id, 0); + + if (!is_valid_key_path(components)) + { + // FIXME: Respond invalid path + g_slist_free_full(components, g_free); + return FALSE; + } + + // Attempt to traverse to the key + // + gint last_id = 0; + gboolean success = TRUE; + + for (GSList* iter = components; iter; iter = iter->next) + { + const gchar* key_name = (gchar*) iter->data; + gint this_id = 0; + + if (!db_get_regkey_id(key_name, last_id, &this_id)) + { + success = FALSE; + break; + } + + last_id = this_id; + } + + if (success) + { + WINTC_SAFE_REF_SET(out_id, last_id); + } + + g_slist_free_full(components, g_free); + + return success; +} + +static void handle_sqlite_problem( + gint rc +) +{ + switch (rc) + { + case SQLITE_ERROR: + WINTC_LOG_DEBUG("regsvc: sqlite error: %s", sqlite3_errmsg(s_db)); + break; + + case SQLITE_MISUSE: + g_critical("%s", "regsvc: sqlite misuse detected"); + break; + + default: + g_critical("regsvc: unknown sqlite problem %d", rc); + break; + } +} + +static gboolean is_valid_key_path( + GSList* components +) +{ + if (g_slist_length(components) == 0) + { + return FALSE; + } + + // Root must be HKCU + // + if ( + g_strcmp0( + (gchar*) g_slist_nth_data(components, 0), + "HKEY_CURRENT_USER" + ) != 0 + ) + { + return FALSE; + } + + // Max length of individual components is 255 + // + for (GSList* iter = components; iter; iter = iter->next) + { + if (strlen((gchar*) iter->data) > 255) + { + return FALSE; + } + } + + return TRUE; +} + +static gboolean is_valid_value_name( + const gchar* value_name +) +{ + // Can't contain backslashes + // + if (strchr(value_name, '\\')) + { + return FALSE; + } + + // Max length of string is 255 + // + if (strlen(value_name) > 255) + { + return FALSE; + } + + return TRUE; +} + +static GSList* path_to_components( + const gchar* key_path +) +{ + GSList* components = NULL; + gchar** split = g_strsplit(key_path, "\\", -1); + + // Move strings into list + // + for (gint i = 0; split[i] != NULL; i++) + { + components = g_slist_append(components, split[i]); + } + + g_free(split); // The list now owns the strings + + // Naughty hack... if the first component is HKCU, expand it to + // HKEY_CURRENT_USER + // + GSList* first = g_slist_nth(components, 0); + + if ( + g_strcmp0( + (gchar*) first->data, + "HKCU" + ) == 0 + ) + { + g_free(first->data); + first->data = g_strdup("HKEY_CURRENT_USER"); + } + + return components; +} diff --git a/base/regsvc/src/backend.h b/base/regsvc/src/backend.h new file mode 100644 index 0000000..519cb08 --- /dev/null +++ b/base/regsvc/src/backend.h @@ -0,0 +1,29 @@ +#ifndef __BACKEND_H__ +#define __BACKEND_H__ + +#include +#include + +// +// INTERNAL FUNCTIONS +// +void backend_close(void); +gboolean backend_init(void); + +gboolean backend_create_key( + const gchar* key_path +); +gboolean backend_get_key_value( + const gchar* key_path, + const gchar* value_name, + WinTCRegistryValueType value_type, + void* value_data +); +gboolean backend_set_key_value( + const gchar* key_path, + const gchar* value_name, + WinTCRegistryValueType value_type, + void* value_data +); + +#endif diff --git a/base/regsvc/src/main.c b/base/regsvc/src/main.c new file mode 100644 index 0000000..92117cc --- /dev/null +++ b/base/regsvc/src/main.c @@ -0,0 +1,376 @@ +#include +#include +#include +#include +#include + +#include "backend.h" + +// +// STATIC DATA +// +static GMainLoop* s_main_loop = NULL; + +// +// FORWARD DECLARATIONS +// +static void on_name_acquired( + GDBusConnection* connection, + const gchar* name, + gpointer user_data +); + +static gboolean on_handle_create_key( + ZWinRegistryGDBUS* reg_dbus, + GDBusMethodInvocation* invocation, + const gchar* key_path, + gpointer user_data +); +static gboolean on_handle_get_key_value( + ZWinRegistryGDBUS* reg_dbus, + GDBusMethodInvocation* invocation, + const gchar* key_path, + const gchar* value_name, + WinTCRegistryValueType value_type, + gpointer user_data +); +static gboolean on_handle_set_key_value( + ZWinRegistryGDBUS* reg_dbus, + GDBusMethodInvocation* invocation, + const gchar* key_path, + const gchar* value_name, + GVariant* value_data, + gboolean silent, + gpointer user_data +); + +static gboolean on_sigterm( + gpointer user_data +); + +// +// ENTRY POINT +// +int main( + WINTC_UNUSED(int argc), + WINTC_UNUSED(char* argv[]) +) +{ + s_main_loop = g_main_loop_new(NULL, FALSE); + + if (!backend_init()) + { + return 1; + } + + WINTC_LOG_DEBUG("regsvc: hosting name %s", "uk.oddmatics.wintc.registry"); + + g_bus_own_name( + G_BUS_TYPE_SESSION, + "uk.oddmatics.wintc.registry", + G_BUS_NAME_OWNER_FLAGS_NONE, + NULL, + on_name_acquired, + NULL, + NULL, + NULL + ); + + g_unix_signal_add( + SIGTERM, + on_sigterm, + NULL + ); + g_main_loop_run(s_main_loop); + + g_main_loop_unref(s_main_loop); + + backend_close(); + + return 0; +} + +// +// CALLBACKS +// +static void on_name_acquired( + GDBusConnection* connection, + WINTC_UNUSED(const gchar* name), + WINTC_UNUSED(gpointer user_data) +) +{ + GError* error = NULL; + ZWinRegistryGDBUS* reg_dbus = zwin_registry_gdbus_skeleton_new(); + + g_signal_connect( + reg_dbus, + "handle-create-key", + G_CALLBACK(on_handle_create_key), + NULL + ); + g_signal_connect( + reg_dbus, + "handle-get-key-value", + G_CALLBACK(on_handle_get_key_value), + NULL + ); + g_signal_connect( + reg_dbus, + "handle-set-key-value", + G_CALLBACK(on_handle_set_key_value), + NULL + ); + + if ( + !g_dbus_interface_skeleton_export( + G_DBUS_INTERFACE_SKELETON(reg_dbus), + connection, + "/uk/oddmatics/wintc/registry/GDBUS", + &error + ) + ) + { + wintc_log_error_and_clear(&error); + } +} + +static gboolean on_handle_create_key( + ZWinRegistryGDBUS* reg_dbus, + GDBusMethodInvocation* invocation, + const gchar* key_path, + WINTC_UNUSED(gpointer user_data) +) +{ + WINTC_LOG_DEBUG("regsvc: received create key request for %s", key_path); + + // Very simply just try creating the key + // + gboolean success = backend_create_key(key_path); + + zwin_registry_gdbus_complete_create_key( + reg_dbus, + invocation, + success ? 1 : 0 + ); + + return TRUE; +} + +static gboolean on_handle_get_key_value( + ZWinRegistryGDBUS* reg_dbus, + GDBusMethodInvocation* invocation, + const gchar* key_path, + const gchar* value_name, + WinTCRegistryValueType value_type, + WINTC_UNUSED(gpointer user_data) +) +{ + WINTC_LOG_DEBUG( + "regsvc: received get key value for %s->%s", + key_path, + value_name + ); + + // Wrap up in a variant + // + gboolean success = FALSE; + gint value_data_int = 0; + gint64 value_data_int64 = 0; + gchar* value_data_str = NULL; + GVariant* variant_ret = NULL; + + switch (value_type) + { + case WINTC_REG_DWORD: + success = + backend_get_key_value( + key_path, + value_name, + WINTC_REG_DWORD, + &value_data_int + ); + + if (success) + { + variant_ret = + g_variant_new( + "v", + g_variant_new_int32(value_data_int) + ); + } + + break; + + case WINTC_REG_QWORD: + success = + backend_get_key_value( + key_path, + value_name, + WINTC_REG_QWORD, + &value_data_int64 + ); + + if (success) + { + variant_ret = + g_variant_new( + "v", + g_variant_new_int64(value_data_int64) + ); + } + + break; + + case WINTC_REG_SZ: + success = + backend_get_key_value( + key_path, + value_name, + WINTC_REG_SZ, + &value_data_str + ); + + if (success) + { + variant_ret = + g_variant_new( + "v", + g_variant_new_string(value_data_str) + ); + + g_free(value_data_str); + } + + break; + + default: + g_critical("regsvc: unknown type %d", value_type); + success = FALSE; + break; + } + + if (!success) + { + // Cannot use maybe-type variants because they're not supported by + // DBus/GDBus, so just respond with 0 - libwintc-registry will discard + // the variant regardless if the result code is 0 so we could respond + // with any variant here and be fine + // + variant_ret = g_variant_new("v", g_variant_new_int32(0)); + } + + zwin_registry_gdbus_complete_get_key_value( + reg_dbus, + invocation, + variant_ret, + success ? 1 : 0 + ); + + return TRUE; +} + +static gboolean on_handle_set_key_value( + ZWinRegistryGDBUS* reg_dbus, + GDBusMethodInvocation* invocation, + const gchar* key_path, + const gchar* value_name, + GVariant* value_data, + gboolean silent, + WINTC_UNUSED(gpointer user_data) +) +{ + WINTC_LOG_DEBUG( + "regsvc: received set key value for %s->%s", + key_path, + value_name + ); + + // The variant incoming determines the type etc. + // + GVariant* inner = g_variant_get_variant(value_data); + gboolean success = FALSE; + WinTCRegistryValueType value_type = wintc_registry_get_type_for_variant( + inner + ); + + gint value_data_int = 0; + gint64 value_data_int64 = 0; + gchar* value_data_str = NULL; + + switch (value_type) + { + case WINTC_REG_DWORD: + value_data_int = g_variant_get_int32(inner); + + success = + backend_set_key_value( + key_path, + value_name, + WINTC_REG_DWORD, + &value_data_int + ); + + break; + + case WINTC_REG_QWORD: + value_data_int64 = g_variant_get_int64(inner); + + success = + backend_set_key_value( + key_path, + value_name, + WINTC_REG_QWORD, + &value_data_int64 + ); + + break; + + case WINTC_REG_SZ: + value_data_str = g_variant_dup_string(inner, NULL); + + success = + backend_set_key_value( + key_path, + value_name, + WINTC_REG_SZ, + &value_data_str + ); + + g_free(value_data_str); + break; + + default: + g_critical("%s", "regsvc: set key unknown variant type"); + break; + } + + zwin_registry_gdbus_complete_set_key_value( + reg_dbus, + invocation, + success ? 1 : 0 + ); + + if (success && !silent) + { + g_signal_emit_by_name( + reg_dbus, + "key-value-changed", + key_path, + value_name, + value_data, + NULL + ); + } + + return TRUE; +} + +static gboolean on_sigterm( + WINTC_UNUSED(gpointer user_data) +) +{ + WINTC_LOG_DEBUG("%s", "regsvc: sigterm, exiting..."); + + g_main_loop_quit(s_main_loop); + return FALSE; +} diff --git a/packaging/bsdpkg/pkgimpl.sh b/packaging/bsdpkg/pkgimpl.sh index 22f352a..fb74ab6 100755 --- a/packaging/bsdpkg/pkgimpl.sh +++ b/packaging/bsdpkg/pkgimpl.sh @@ -34,7 +34,7 @@ do_packaging() # Build package now # - (cd "${pkg_dir}/usr/local" && find . -type f,l) | sed 's/\.\///' > plist + (cd "${pkg_dir}/usr/local" && find -L . -type f) | sed 's/\.\///' > plist pkg create -M manifest -r "${pkg_dir}" -p plist cd "${CURDIR}" diff --git a/packaging/cmake-inc/codegen/CMakeLists.txt b/packaging/cmake-inc/codegen/CMakeLists.txt new file mode 100644 index 0000000..09ae5c3 --- /dev/null +++ b/packaging/cmake-inc/codegen/CMakeLists.txt @@ -0,0 +1,55 @@ +cmake_minimum_required(VERSION 3.5) + +# Define function for gdbus-codegen +# +function(wintc_gdbus_codegen XML_FILE OUT_FILE_NOEXT CP_PUBLIC) + find_program(GDBUS_CODEGEN gdbus-codegen REQUIRED) + + add_custom_command( + OUTPUT + ${CMAKE_CURRENT_SOURCE_DIR}/src/${OUT_FILE_NOEXT}.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/${OUT_FILE_NOEXT}.h + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src + COMMAND ${GDBUS_CODEGEN} + ARGS + --interface-prefix uk.oddmatics.wintc. + --generate-c-code ${OUT_FILE_NOEXT} + --c-namespace ZWin + --c-generate-object-manager + ${XML_FILE} + VERBATIM + DEPENDS + src/${XML_FILE} + ) + + add_custom_target( + dbus-codegen + DEPENDS + src/${OUT_FILE_NOEXT}.c + src/${OUT_FILE_NOEXT}.h + ) + + # If the header is needed in public/ for libraries then copy it + # + if (CP_PUBLIC) + find_program(CP cp REQUIRED) + + add_custom_command( + OUTPUT + ${CMAKE_CURRENT_SOURCE_DIR}/public/${OUT_FILE_NOEXT}.h + COMMAND ${CP} + ARGS + ${CMAKE_CURRENT_SOURCE_DIR}/src/${OUT_FILE_NOEXT}.h + ${CMAKE_CURRENT_SOURCE_DIR}/public/${OUT_FILE_NOEXT}.h + VERBATIM + DEPENDS + src/${OUT_FILE_NOEXT}.h + ) + + add_custom_target( + dbus-codegen-header + DEPENDS + public/${OUT_FILE_NOEXT}.h + ) + endif() +endfunction() diff --git a/packaging/cmake-inc/common/CMakeLists.txt b/packaging/cmake-inc/common/CMakeLists.txt index 624ca6a..37b9edc 100644 --- a/packaging/cmake-inc/common/CMakeLists.txt +++ b/packaging/cmake-inc/common/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.12) set(REPO_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../..) @@ -26,7 +26,7 @@ if (NOT CMAKE_BUILD_TYPE) endif() if (${CMAKE_BUILD_TYPE} STREQUAL Debug) - add_definitions(-DWINTC_CHECKED) + add_compile_definitions(WINTC_CHECKED) endif() # Handle SKU stuff diff --git a/packaging/cmake-inc/packaging/CMakeLists.txt b/packaging/cmake-inc/packaging/CMakeLists.txt index 6c0c829..a1cd759 100644 --- a/packaging/cmake-inc/packaging/CMakeLists.txt +++ b/packaging/cmake-inc/packaging/CMakeLists.txt @@ -16,6 +16,10 @@ if (NOT DEFINED WINTC_PKGMGR OR "${WINTC_PKGMGR}" STREQUAL "") set(WINTC_PKGMGR raw) endif() +# Export install prefix (useful for retrieving data at runtime) +# +add_compile_definitions(WINTC_RT_PREFIX="${CMAKE_INSTALL_PREFIX}") + # Set up library directory # if ( diff --git a/packaging/targets b/packaging/targets index 15ae11d..37eb251 100644 --- a/packaging/targets +++ b/packaging/targets @@ -1,6 +1,7 @@ base/bootvid base/bldtag base/logonui +base/regsvc cursors/no-shadow/standard cursors/with-shadow/standard fonts @@ -9,6 +10,7 @@ sounds themes/native themes/professional themes/luna/blue +shell/cpl/desk shell/cpl/printers shell/cpl/sysdm shell/desktop diff --git a/private/play/dbus/.gitignore b/private/play/dbus/.gitignore new file mode 100644 index 0000000..3004d17 --- /dev/null +++ b/private/play/dbus/.gitignore @@ -0,0 +1,2 @@ +# Ignore generated output +testreg-skel.* diff --git a/private/play/dbus/clt/CMakeLists.txt b/private/play/dbus/clt/CMakeLists.txt new file mode 100644 index 0000000..439a1cf --- /dev/null +++ b/private/play/dbus/clt/CMakeLists.txt @@ -0,0 +1,74 @@ +cmake_minimum_required(VERSION 3.5) + +project( + wintc-dbus-test-clt + VERSION 1.0 + DESCRIPTION "Windows Total Conversion DBus test client." + LANGUAGES C +) + +set(PROJECT_ANYARCH false) +set(PROJECT_FREESTATUS true) +set(PROJECT_MAINTAINER "Rory Fewell ") + +set(PROJECT_ROOT ${CMAKE_CURRENT_LIST_DIR}) + +include(GNUInstallDirs) + +include(../../../../packaging/cmake-inc/common/CMakeLists.txt) +include(../../../../packaging/cmake-inc/codegen/CMakeLists.txt) +include(../../../../packaging/cmake-inc/linking/CMakeLists.txt) +include(../../../../packaging/cmake-inc/packaging/CMakeLists.txt) + +wintc_resolve_library(glib-2.0 GLIB) +wintc_resolve_library(gtk+-3.0 GTK3) +wintc_resolve_library(wintc-comgtk WINTC_COMGTK) + +wintc_gdbus_codegen( + testreg.xml + testreg-skel +) + +add_executable( + wintc-dbus-test-clt + src/main.c + src/testreg-skel.c + src/testreg-skel.h +) + +target_compile_options( + wintc-dbus-test-clt + PRIVATE ${WINTC_COMPILE_OPTIONS} +) + +target_include_directories( + wintc-dbus-test-clt + SYSTEM + BEFORE + PRIVATE ${GLIB_INCLUDE_DIRS} + PRIVATE ${GTK3_INCLUDE_DIRS} + PRIVATE ${WINTC_COMGTK_INCLUDE_DIRS} +) + +target_link_directories( + wintc-dbus-test-clt + PRIVATE ${GLIB_LIBRARY_DIRS} + PRIVATE ${GTK3_LIBRARY_DIRS} + PRIVATE ${WINTC_COMGTK_LIBRARY_DIRS} +) + +target_link_libraries( + wintc-dbus-test-clt + PRIVATE ${GLIB_LIBRARIES} + PRIVATE ${GTK3_LIBRARIES} + PRIVATE ${WINTC_COMGTK_LIBRARIES} +) + +# Installation +# +wintc_configure_and_install_packaging() + +install( + TARGETS wintc-dbus-test-clt + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) diff --git a/private/play/dbus/clt/src/main.c b/private/play/dbus/clt/src/main.c new file mode 100644 index 0000000..1bee414 --- /dev/null +++ b/private/play/dbus/clt/src/main.c @@ -0,0 +1,49 @@ +#include +#include + +#include "testreg-skel.h" + +// +// ENTRY POINT +// +int main() +{ + GError* error = NULL; + WinTCTestregGDBUS* proxy; + gchar* szoutput; + + proxy = + win_tc_testreg_gdbus_proxy_new_for_bus_sync( + G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + "uk.oddmatics.wintc.testreg", + "/uk/oddmatics/wintc/testreg/GDBUS", + NULL, + &error + ); + + if (!proxy) + { + wintc_log_error_and_clear(&error); + return 1; + } + + win_tc_testreg_gdbus_call_testing_sync( + proxy, + "hello!", + &szoutput, + NULL, + &error + ); + + if (error) + { + wintc_log_error_and_clear(&error); + return 1; + } + + g_message("Client got: %s", szoutput); + + g_object_unref(proxy); + return 0; +} diff --git a/private/play/dbus/clt/src/testreg.xml b/private/play/dbus/clt/src/testreg.xml new file mode 100644 index 0000000..bb7ed10 --- /dev/null +++ b/private/play/dbus/clt/src/testreg.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/private/play/dbus/srv/CMakeLists.txt b/private/play/dbus/srv/CMakeLists.txt new file mode 100644 index 0000000..b8463ce --- /dev/null +++ b/private/play/dbus/srv/CMakeLists.txt @@ -0,0 +1,75 @@ +cmake_minimum_required(VERSION 3.5) + +project( + wintc-dbus-test-srv + VERSION 1.0 + DESCRIPTION "Windows Total Conversion DBus test server." + LANGUAGES C +) + +set(PROJECT_ANYARCH false) +set(PROJECT_FREESTATUS true) +set(PROJECT_MAINTAINER "Rory Fewell ") + +set(PROJECT_ROOT ${CMAKE_CURRENT_LIST_DIR}) + +include(GNUInstallDirs) + +include(../../../../packaging/cmake-inc/common/CMakeLists.txt) +include(../../../../packaging/cmake-inc/codegen/CMakeLists.txt) +include(../../../../packaging/cmake-inc/linking/CMakeLists.txt) +include(../../../../packaging/cmake-inc/packaging/CMakeLists.txt) + +wintc_resolve_library(glib-2.0 GLIB) +wintc_resolve_library(gtk+-3.0 GTK3) +wintc_resolve_library(wintc-comgtk WINTC_COMGTK) + +wintc_gdbus_codegen( + testreg.xml + testreg-skel + FALSE +) + +add_executable( + wintc-dbus-test-srv + src/main.c + src/testreg-skel.c + src/testreg-skel.h +) + +target_compile_options( + wintc-dbus-test-srv + PRIVATE ${WINTC_COMPILE_OPTIONS} +) + +target_include_directories( + wintc-dbus-test-srv + SYSTEM + BEFORE + PRIVATE ${GLIB_INCLUDE_DIRS} + PRIVATE ${GTK3_INCLUDE_DIRS} + PRIVATE ${WINTC_COMGTK_INCLUDE_DIRS} +) + +target_link_directories( + wintc-dbus-test-srv + PRIVATE ${GLIB_LIBRARY_DIRS} + PRIVATE ${GTK3_LIBRARY_DIRS} + PRIVATE ${WINTC_COMGTK_LIBRARY_DIRS} +) + +target_link_libraries( + wintc-dbus-test-srv + PRIVATE ${GLIB_LIBRARIES} + PRIVATE ${GTK3_LIBRARIES} + PRIVATE ${WINTC_COMGTK_LIBRARIES} +) + +# Installation +# +wintc_configure_and_install_packaging() + +install( + TARGETS wintc-dbus-test-srv + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) diff --git a/private/play/dbus/srv/deps b/private/play/dbus/srv/deps new file mode 100644 index 0000000..4a39e02 --- /dev/null +++ b/private/play/dbus/srv/deps @@ -0,0 +1,3 @@ +bt,rt:glib2 +bt,rt:gtk3 +bt,rt:wintc-comgtk diff --git a/private/play/dbus/srv/src/main.c b/private/play/dbus/srv/src/main.c new file mode 100644 index 0000000..cf9d626 --- /dev/null +++ b/private/play/dbus/srv/src/main.c @@ -0,0 +1,98 @@ +#include +#include + +#include "testreg-skel.h" + +// +// FORWARD DECLARATIONS +// +static void on_name_acquired( + GDBusConnection* connection, + const gchar* name, + gpointer user_data +); + +static gboolean on_handle_testing( + WinTCTestregGDBUS* iface, + GDBusMethodInvocation* invocation, + const gchar* szinput, + gpointer user_data +); + +// +// ENTRY POINT +// +int main() +{ + GMainLoop* loop; + + loop = g_main_loop_new(NULL, FALSE); + + g_bus_own_name( + G_BUS_TYPE_SESSION, + "uk.oddmatics.wintc.testreg", + G_BUS_NAME_OWNER_FLAGS_NONE, + NULL, + on_name_acquired, + NULL, + NULL, + NULL + ); + + g_main_loop_run(loop); + + return 0; +} + +// +// PRIVATE FUNCTIONS +// +static void on_name_acquired( + GDBusConnection* connection, + WINTC_UNUSED(const gchar* name), + WINTC_UNUSED(gpointer user_data) +) +{ + GError* error = NULL; + WinTCTestregGDBUS* iface = win_tc_testreg_gdbus_skeleton_new(); + + g_signal_connect( + iface, + "handle-testing", + G_CALLBACK(on_handle_testing), + NULL + ); + + if ( + !g_dbus_interface_skeleton_export( + G_DBUS_INTERFACE_SKELETON(iface), + connection, + "/uk/oddmatics/wintc/testreg/GDBUS", + &error + ) + ) + { + wintc_log_error_and_clear(&error); + } +} + +static gboolean on_handle_testing( + WinTCTestregGDBUS* iface, + GDBusMethodInvocation* invocation, + const gchar* szinput, + WINTC_UNUSED(gpointer user_data) +) +{ + gchar* szoutput = g_strdup_printf("GDBUS IN: %s", szinput); + + win_tc_testreg_gdbus_complete_testing( + iface, + invocation, + szoutput + ); + + g_message("%s", szoutput); + g_free(szoutput); + + return TRUE; +} diff --git a/private/play/dbus/srv/src/testreg.xml b/private/play/dbus/srv/src/testreg.xml new file mode 100644 index 0000000..bb7ed10 --- /dev/null +++ b/private/play/dbus/srv/src/testreg.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/shared/comctl/CMakeLists.txt b/shared/comctl/CMakeLists.txt index fc035cd..ac45c32 100644 --- a/shared/comctl/CMakeLists.txt +++ b/shared/comctl/CMakeLists.txt @@ -25,6 +25,8 @@ wintc_resolve_library(gdk-pixbuf-2.0 GDK_PIXBUF) wintc_resolve_library(glib-2.0 GLIB) wintc_resolve_library(gtk+-3.0 GTK3) wintc_resolve_library(wintc-comgtk WINTC_COMGTK) +wintc_resolve_library(wintc-shcommon WINTC_SHCOMMON) +wintc_resolve_library(wintc-shlang WINTC_SHLANG) wintc_compile_resources() @@ -33,6 +35,8 @@ add_library( src/resources.c src/animctl.c public/animctl.h + src/cpl.c + public/cpl.h src/style.c public/style.h ) @@ -57,6 +61,8 @@ target_include_directories( PRIVATE ${GLIB_INCLUDE_DIRS} PRIVATE ${GTK3_INCLUDE_DIRS} PRIVATE ${WINTC_COMGTK_INCLUDE_DIRS} + PRIVATE ${WINTC_SHCOMMON_INCLUDE_DIRS} + PRIVATE ${WINTC_SHLANG_INCLUDE_DIRS} ) target_link_directories( @@ -65,6 +71,8 @@ target_link_directories( PRIVATE ${GLIB_LIBRARY_DIRS} PRIVATE ${GTK3_LIBRARY_DIRS} PRIVATE ${WINTC_COMGTK_LIBRARY_DIRS} + PRIVATE ${WINTC_SHCOMMON_LIBRARY_DIRS} + PRIVATE ${WINTC_SHLANG_LIBRARY_DIRS} ) target_link_libraries( @@ -73,6 +81,8 @@ target_link_libraries( PRIVATE ${GLIB_LIBRARIES} PRIVATE ${GTK3_LIBRARIES} PRIVATE ${WINTC_COMGTK_LIBRARIES} + PRIVATE ${WINTC_SHCOMMON_LIBRARIES} + PRIVATE ${WINTC_SHLANG_LIBRARIES} ) # Installation diff --git a/shared/comctl/deps b/shared/comctl/deps index 7e95f86..e1fb0cd 100644 --- a/shared/comctl/deps +++ b/shared/comctl/deps @@ -2,3 +2,4 @@ bt,rt:gdk-pixbuf2 bt,rt:glib2 bt,rt:gtk3 bt,rt:wintc-comgtk +bt,rt:wintc-shlang diff --git a/shared/comctl/public/cpl.h b/shared/comctl/public/cpl.h new file mode 100644 index 0000000..a23878b --- /dev/null +++ b/shared/comctl/public/cpl.h @@ -0,0 +1,13 @@ +#ifndef __COMCTL_CPL_H__ +#define __COMCTL_CPL_H__ + +// +// PUBLIC FUNCTIONS +// +void wintc_ctl_cpl_notebook_append_page_from_resource( + GtkNotebook* notebook, + const gchar* resource_path, + ... +); + +#endif diff --git a/shared/comctl/public/libapi.h.in b/shared/comctl/public/libapi.h.in index d638a30..7167f29 100644 --- a/shared/comctl/public/libapi.h.in +++ b/shared/comctl/public/libapi.h.in @@ -2,6 +2,7 @@ #define __WINTC_COMCTL_H__ #include "@LIB_HEADER_DIR@/animctl.h" +#include "@LIB_HEADER_DIR@/cpl.h" #include "@LIB_HEADER_DIR@/style.h" #endif diff --git a/shared/comctl/src/cpl.c b/shared/comctl/src/cpl.c new file mode 100644 index 0000000..99b46e3 --- /dev/null +++ b/shared/comctl/src/cpl.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include +#include + +#include "../public/cpl.h" + +// +// PUBLIC FUNCTIONS +// +void wintc_ctl_cpl_notebook_append_page_from_resource( + GtkNotebook* notebook, + const gchar* resource_path, + ... +) +{ + GtkBuilder* builder; + GtkWidget* box_page; + GtkWidget* label_title; + + builder = gtk_builder_new_from_resource(resource_path); + + // Handle construction into UI + // + wintc_lc_builder_preprocess_widget_text(builder); + + box_page = GTK_WIDGET(gtk_builder_get_object(builder, "page-box")); + label_title = GTK_WIDGET(gtk_builder_get_object(builder, "label-title")); + + gtk_notebook_append_page( + notebook, + box_page, + label_title + ); + + // Pull out objects as requested, args are provided as pairs (name, dstptr) + // + va_list ap; + GtkWidget** next_dst; + gchar* next_name; + + va_start(ap, resource_path); + + next_name = va_arg(ap, gchar*); + + while (next_name) + { + next_dst = va_arg(ap, GtkWidget**); + *next_dst = GTK_WIDGET(gtk_builder_get_object(builder, next_name)); + + // Iter + // + next_name = va_arg(ap, gchar*); + } + + va_end(ap); + + g_object_unref(G_OBJECT(builder)); +} diff --git a/shared/registry/.gitignore b/shared/registry/.gitignore new file mode 100644 index 0000000..2e89710 --- /dev/null +++ b/shared/registry/.gitignore @@ -0,0 +1,2 @@ +# Ignore generated output +reg-dbus.* diff --git a/shared/registry/CMakeLists.txt b/shared/registry/CMakeLists.txt new file mode 100644 index 0000000..fea7567 --- /dev/null +++ b/shared/registry/CMakeLists.txt @@ -0,0 +1,91 @@ +cmake_minimum_required(VERSION 3.5) + +project( + libwintc-registry + VERSION 1.0 + DESCRIPTION "Windows Total Conversion registry library." + LANGUAGES C +) + +set(PROJECT_ANYARCH false) +set(PROJECT_FREESTATUS true) +set(PROJECT_MAINTAINER "Rory Fewell ") + +set(PROJECT_ROOT ${CMAKE_CURRENT_LIST_DIR}) + +set(WINTC_NO_PEDANTIC_COMPILE true) # Necessary for gdbus-codegen + +include(GNUInstallDirs) + +include(../../packaging/cmake-inc/common/CMakeLists.txt) +include(../../packaging/cmake-inc/codegen/CMakeLists.txt) +include(../../packaging/cmake-inc/libraries/CMakeLists.txt) +include(../../packaging/cmake-inc/linking/CMakeLists.txt) +include(../../packaging/cmake-inc/packaging/CMakeLists.txt) + +wintc_resolve_library(glib-2.0 GLIB) +wintc_resolve_library(gtk+-3.0 GTK3) +wintc_resolve_library(wintc-comgtk WINTC_COMGTK) + +wintc_gdbus_codegen( + dbus-if.xml + reg-dbus + TRUE +) + +add_library( + libwintc-registry + public/reg-dbus.h + src/reg-dbus.c + src/reg-dbus.h + src/regwrap.c + public/regwrap.h + src/variant.c + public/variant.h +) + +set_target_properties( + libwintc-registry + PROPERTIES + SOVERSION 1 + VERSION ${PROJECT_VERSION} +) + +target_compile_options( + libwintc-registry + PRIVATE ${WINTC_COMPILE_OPTIONS} +) + +target_include_directories( + libwintc-registry + SYSTEM + BEFORE + PRIVATE ${GLIB_INCLUDE_DIRS} + PRIVATE ${GTK3_INCLUDE_DIRS} + PRIVATE ${WINTC_COMGTK_INCLUDE_DIRS} +) + +target_link_directories( + libwintc-registry + PRIVATE ${GLIB_LIBRARY_DIRS} + PRIVATE ${GTK3_LIBRARY_DIRS} + PRIVATE ${WINTC_COMGTK_LIBRARY_DIRS} +) + +target_link_libraries( + libwintc-registry + PRIVATE ${GLIB_LIBRARIES} + PRIVATE ${GTK3_LIBRARIES} + PRIVATE ${WINTC_COMGTK_LIBRARIES} +) + +# Installation +# +wintc_configure_and_install_packaging() +wintc_add_pkgconfig_install() +wintc_install_public_headers() + +install( + TARGETS libwintc-registry + LIBRARY DESTINATION ${LIB_DIR} +) diff --git a/shared/registry/README.MD b/shared/registry/README.MD new file mode 100644 index 0000000..a0f5754 --- /dev/null +++ b/shared/registry/README.MD @@ -0,0 +1,5 @@ +# libwintc-registry +This directory contains the source code for the registry library. + +## Purpose +This library provides the GDBus interface and a simple client-side wrapper for interacting with the registry. diff --git a/shared/registry/deps b/shared/registry/deps new file mode 100644 index 0000000..f11c63c --- /dev/null +++ b/shared/registry/deps @@ -0,0 +1,4 @@ +bt,rt:glib2 +bt,rt:gtk3 +bt,rt:python3-packaging +bt,rt:wintc-comgtk diff --git a/shared/registry/public/libapi.h.in b/shared/registry/public/libapi.h.in new file mode 100644 index 0000000..038fe52 --- /dev/null +++ b/shared/registry/public/libapi.h.in @@ -0,0 +1,8 @@ +#ifndef __WINTC_REGISTRY_H__ +#define __WINTC_REGISTRY_H__ + +#include "@LIB_HEADER_DIR@/reg-dbus.h" +#include "@LIB_HEADER_DIR@/regwrap.h" +#include "@LIB_HEADER_DIR@/variant.h" + +#endif diff --git a/shared/registry/public/regwrap.h b/shared/registry/public/regwrap.h new file mode 100644 index 0000000..5abe92a --- /dev/null +++ b/shared/registry/public/regwrap.h @@ -0,0 +1,80 @@ +#ifndef __REGISTRY_REGWRAP_H__ +#define __REGISTRY_REGWRAP_H__ + +#include + +// +// PUBLIC ENUMS +// +typedef enum +{ + WINTC_REG_INVALID, + WINTC_REG_DWORD, + WINTC_REG_QWORD, + WINTC_REG_SZ +} WinTCRegistryValueType; + +// +// GLIB OOP BOILERPLATE +// +typedef struct _WinTCRegistryClass WinTCRegistryClass; +typedef struct _WinTCRegistry WinTCRegistry; + +#define WINTC_TYPE_REGISTRY (wintc_registry_get_type()) +#define WINTC_REGISTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), WINTC_TYPE_REGISTRY, WinTCRegistry)) +#define WINTC_REGISTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WINTC_TYPE_REGISTRY, WinTCRegistryClass)) +#define IS_WINTC_REGISTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), WINTC_TYPE_REGISTRY)) +#define IS_WINTC_REGISTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), WINTC_TYPE_REGISTRY)) +#define WINTC_REGISTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), WINTC_TYPE_REGISTRY, WinTCRegistry)) + +GType wintc_registry_get_type(void) G_GNUC_CONST; + +// +// CALLBACK PROTOTYPES +// +typedef void (*WinTCRegistryKeyCallback) ( + WinTCRegistry* registry, + const gchar* key_path, + const gchar* value_name, + GVariant* value_variant, + gpointer user_data +); + +// +// PUBLIC FUNCTIONS +// +WinTCRegistry* wintc_registry_new(void); + +gboolean wintc_registry_create_key( + WinTCRegistry* registry, + const gchar* key_path, + GError** error +); + +gboolean wintc_registry_get_key_value( + WinTCRegistry* registry, + const gchar* key_path, + const gchar* value_name, + WinTCRegistryValueType value_type, + void* value_data, + GError** error +); + +gboolean wintc_registry_set_key_value( + WinTCRegistry* registry, + const gchar* key_path, + const gchar* value_name, + WinTCRegistryValueType value_type, + const void* value_data, + gboolean silent, + GError** error +); + +void wintc_registry_watch_key( + WinTCRegistry* registry, + const gchar* key_path, + WinTCRegistryKeyCallback callback, + gpointer user_data +); + +#endif diff --git a/shared/registry/public/variant.h b/shared/registry/public/variant.h new file mode 100644 index 0000000..88b7151 --- /dev/null +++ b/shared/registry/public/variant.h @@ -0,0 +1,15 @@ +#ifndef __REGISTRY_VARIANT_H__ +#define __REGISTRY_VARIANT_H__ + +#include + +#include "regwrap.h" + +// +// PUBLIC FUNCTIONS +// +WinTCRegistryValueType wintc_registry_get_type_for_variant( + GVariant* variant +); + +#endif diff --git a/shared/registry/src/dbus-if.xml b/shared/registry/src/dbus-if.xml new file mode 100644 index 0000000..4ee9274 --- /dev/null +++ b/shared/registry/src/dbus-if.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/shared/registry/src/regwrap.c b/shared/registry/src/regwrap.c new file mode 100644 index 0000000..2f26b94 --- /dev/null +++ b/shared/registry/src/regwrap.c @@ -0,0 +1,433 @@ +#include +#include + +#include "../public/reg-dbus.h" +#include "../public/regwrap.h" + +// +// PRIVATE STRUCTS +// +typedef struct _RegistryCBData +{ + WinTCRegistryKeyCallback callback; + gpointer user_data; +} RegistryCBData; + +// +// FORWARD DECLARATIONS +// +static void wintc_registry_dispose( + GObject* object +); + +static gboolean wintc_registry_ensure_proxy( + WinTCRegistry* registry, + GError** error +); + +static void on_reg_key_value_changed( + ZWinRegistryGDBUS* proxy, + const gchar* key_path, + const gchar* value_name, + GVariant* value_data, + gpointer user_data +); + +// +// GLIB OOP CLASS/INSTANCE DEFINITIONS +// +struct _WinTCRegistryClass +{ + GObjectClass __parent__; +}; + +struct _WinTCRegistry +{ + GObject __parent__; + + gboolean connected_signal; + GHashTable* map_key_to_cb; + ZWinRegistryGDBUS* proxy; +}; + +// +// GLIB TYPE DEFINITIONS & CTORS +// +G_DEFINE_TYPE( + WinTCRegistry, + wintc_registry, + G_TYPE_OBJECT +) + +static void wintc_registry_class_init( + WinTCRegistryClass* klass +) +{ + GObjectClass* object_class = G_OBJECT_CLASS(klass); + + object_class->dispose = wintc_registry_dispose; +} + +static void wintc_registry_init( + WinTCRegistry* self +) +{ + self->map_key_to_cb = + g_hash_table_new_full( + g_str_hash, + g_str_equal, + g_free, + g_free + ); +} + +// +// CLASS VIRTUAL METHODS +// +static void wintc_registry_dispose( + GObject* object +) +{ + WinTCRegistry* registry = WINTC_REGISTRY(object); + + g_hash_table_destroy( + registry->map_key_to_cb + ); + + (G_OBJECT_CLASS(wintc_registry_parent_class))->dispose(object); +} + +// +// PUBLIC FUNCTIONS +// +WinTCRegistry* wintc_registry_new(void) +{ + return WINTC_REGISTRY( + g_object_new( + WINTC_TYPE_REGISTRY, + NULL + ) + ); +} + +gboolean wintc_registry_create_key( + WinTCRegistry* registry, + const gchar* key_path, + GError** error +) +{ + GError* local_error = NULL; + + WINTC_SAFE_REF_CLEAR(error); + + if (!wintc_registry_ensure_proxy(registry, &local_error)) + { + g_propagate_error(error, local_error); + return FALSE; + } + + // Perform dbus call + // + gint result = 0; + + if ( + !zwin_registry_gdbus_call_create_key_sync( + registry->proxy, + key_path, + &result, + NULL, + &local_error + ) + ) + { + g_propagate_error(error, local_error); + return FALSE; + } + + return !!result; +} + +gboolean wintc_registry_get_key_value( + WinTCRegistry* registry, + const gchar* key_path, + const gchar* value_name, + WinTCRegistryValueType value_type, + void* value_data, + GError** error +) +{ + GError* local_error = NULL; + + WINTC_SAFE_REF_CLEAR(error); + + switch (value_type) + { + case WINTC_REG_DWORD: *((gint*) value_data) = 0; break; + case WINTC_REG_QWORD: *((gint64*) value_data) = 0; break; + case WINTC_REG_SZ: *((gchar**) value_data) = NULL; break; + + default: + g_critical("registry: unknown type %d", value_type); + return FALSE; + } + + if (!wintc_registry_ensure_proxy(registry, &local_error)) + { + g_propagate_error(error, local_error); + return FALSE; + } + + // Perform dbus call + // + gint result = 0; + GVariant* value_variant = NULL; + + if ( + !zwin_registry_gdbus_call_get_key_value_sync( + registry->proxy, + key_path, + value_name, + value_type, + &value_variant, + &result, + NULL, + &local_error + ) + ) + { + g_propagate_error(error, local_error); + return FALSE; + } + + if (result) + { + GVariant* inner = g_variant_get_variant(value_variant); + + switch (value_type) + { + case WINTC_REG_DWORD: + *((gint*) value_data) = + g_variant_get_int32(inner); + break; + + case WINTC_REG_QWORD: + *((gint64*) value_data) = + g_variant_get_int64(inner); + break; + + case WINTC_REG_SZ: + *((gchar**) value_data) = + g_variant_dup_string(inner, NULL); + break; + + default: + g_critical("registry: unknown type %d", value_type); + break; + } + } + + g_variant_unref(value_variant); + + return TRUE; +} + +gboolean wintc_registry_set_key_value( + WinTCRegistry* registry, + const gchar* key_path, + const gchar* value_name, + WinTCRegistryValueType value_type, + const void* value_data, + gboolean silent, + GError** error +) +{ + GError* local_error = NULL; + + WINTC_SAFE_REF_CLEAR(error); + + if (!wintc_registry_ensure_proxy(registry, &local_error)) + { + g_propagate_error(error, local_error); + return FALSE; + } + + // Set up variant + // + GVariant* value_variant = NULL; + + switch (value_type) + { + case WINTC_REG_DWORD: + value_variant = + g_variant_new( + "v", + g_variant_new_int32( + *((const gint*) value_data) + ) + ); + break; + + case WINTC_REG_QWORD: + value_variant = + g_variant_new( + "v", + g_variant_new_int64( + *((const gint64*) value_data) + ) + ); + break; + + case WINTC_REG_SZ: + value_variant = + g_variant_new( + "v", + g_variant_new_string( + *((const gchar**) value_data) + ) + ); + break; + + default: + g_critical("registry: unknown type %d", value_type); + return FALSE; + } + + // Perform dbus call + // + gint result = 0; + + if ( + !zwin_registry_gdbus_call_set_key_value_sync( + registry->proxy, + key_path, + value_name, + value_variant, + silent, + &result, + NULL, + &local_error + ) + ) + { + g_propagate_error(error, local_error); + return FALSE; + } + + return !!result; +} + +void wintc_registry_watch_key( + WinTCRegistry* registry, + const gchar* key_path, + WinTCRegistryKeyCallback callback, + gpointer user_data +) +{ + GError* error = NULL; + + if (!wintc_registry_ensure_proxy(registry, &error)) + { + wintc_log_error_and_clear(&error); + return; + } + + // Add watcher + // + RegistryCBData* cb_data = g_new(RegistryCBData, 1); + + cb_data->callback = callback; + cb_data->user_data = user_data; + + if (!registry->connected_signal) + { + g_signal_connect( + registry->proxy, + "key-value-changed", + G_CALLBACK(on_reg_key_value_changed), + registry + ); + } + + if ( + !g_hash_table_replace( + registry->map_key_to_cb, + g_strdup(key_path), + cb_data + ) + ) + { + g_warning("registry: overwritten key watcher for %s", key_path); + } +} + +// +// PRIVATE FUNCTIONS +// +static gboolean wintc_registry_ensure_proxy( + WinTCRegistry* registry, + GError** error +) +{ + GError* local_error = NULL; + + WINTC_SAFE_REF_CLEAR(error); + + if (registry->proxy) + { + return TRUE; + } + + registry->proxy = + zwin_registry_gdbus_proxy_new_for_bus_sync( + G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + "uk.oddmatics.wintc.registry", + "/uk/oddmatics/wintc/registry/GDBUS", + NULL, + &local_error + ); + + if (!registry->proxy) + { + g_propagate_error(error, local_error); + return FALSE; + } + + return TRUE; +} + +// +// CALLBACKS +// +static void on_reg_key_value_changed( + WINTC_UNUSED(ZWinRegistryGDBUS* proxy), + const gchar* key_path, + const gchar* value_name, + GVariant* value_data, + gpointer user_data +) +{ + WinTCRegistry* registry = WINTC_REGISTRY(user_data); + + // Look up if there's a handler for this key + // + RegistryCBData* cb_data = + g_hash_table_lookup(registry->map_key_to_cb, key_path); + + if (!cb_data) + { + return; + } + + // Issue the callback + // + GVariant* inner = g_variant_get_variant(value_data); + + cb_data->callback( + registry, + key_path, + value_name, + inner, + cb_data->user_data + ); +} diff --git a/shared/registry/src/variant.c b/shared/registry/src/variant.c new file mode 100644 index 0000000..08abd9e --- /dev/null +++ b/shared/registry/src/variant.c @@ -0,0 +1,28 @@ +#include +#include + +#include "../public/regwrap.h" +#include "../public/variant.h" + +// +// PUBLIC FUNCTIONS +// +WinTCRegistryValueType wintc_registry_get_type_for_variant( + GVariant* variant +) +{ + if (g_variant_is_of_type(variant, G_VARIANT_TYPE_INT32)) + { + return WINTC_REG_DWORD; + } + else if (g_variant_is_of_type(variant, G_VARIANT_TYPE_INT64)) + { + return WINTC_REG_QWORD; + } + else if (g_variant_is_of_type(variant, G_VARIANT_TYPE_STRING)) + { + return WINTC_REG_SZ; + } + + return WINTC_REG_INVALID; +} diff --git a/shared/shcommon/CMakeLists.txt b/shared/shcommon/CMakeLists.txt index 73dd70c..1594f26 100644 --- a/shared/shcommon/CMakeLists.txt +++ b/shared/shcommon/CMakeLists.txt @@ -26,6 +26,8 @@ wintc_resolve_library(wintc-comgtk WINTC_COMGTK) add_library( libwintc-shcommon + src/fs.c + public/fs.h src/path.c public/path.h src/places.c diff --git a/shared/shcommon/public/fs.h b/shared/shcommon/public/fs.h new file mode 100644 index 0000000..206e5a7 --- /dev/null +++ b/shared/shcommon/public/fs.h @@ -0,0 +1,15 @@ +#ifndef __SHCOMMON_FS_H__ +#define __SHCOMMON_FS_H__ + +// +// PUBLIC FUNCTIONS +// +GSList* wintc_sh_fs_get_names_as_list( + const gchar* path, + gboolean full_names, + GFileTest test, + gboolean recursive, + GError** error +); + +#endif diff --git a/shared/shcommon/public/libapi.h.in b/shared/shcommon/public/libapi.h.in index 3bac82d..2272b38 100644 --- a/shared/shcommon/public/libapi.h.in +++ b/shared/shcommon/public/libapi.h.in @@ -1,6 +1,7 @@ #ifndef __WINTC_SHCOMMON_H__ #define __WINTC_SHCOMMON_H__ +#include "@LIB_HEADER_DIR@/fs.h" #include "@LIB_HEADER_DIR@/path.h" #include "@LIB_HEADER_DIR@/places.h" diff --git a/shared/shcommon/src/fs.c b/shared/shcommon/src/fs.c new file mode 100644 index 0000000..ae7790a --- /dev/null +++ b/shared/shcommon/src/fs.c @@ -0,0 +1,93 @@ +#include +#include + +#include "../public/fs.h" + +// +// PUBLIC FUNCTIONS +// +GSList* wintc_sh_fs_get_names_as_list( + const gchar* path, + gboolean full_names, + GFileTest test, + gboolean recursive, + GError** error +) +{ + GDir* dir; + const gchar* dir_entry = NULL; + GSList* dirs_to_enum = NULL; + GSList* entries = NULL; + GSList* iter = NULL; + + WINTC_SAFE_REF_CLEAR(error); + + WINTC_LOG_DEBUG( + "shcommon: enum dir %s%s", + path, + recursive ? " (recursive)" : "" + ); + + // Push requested path as the first item + // + dirs_to_enum = g_slist_append(dirs_to_enum, g_strdup(path)); + iter = dirs_to_enum; + + while (iter) + { + WINTC_LOG_DEBUG("shcommon: enum iter dir %s", (gchar*) iter->data); + + dir = g_dir_open((gchar*) iter->data, 0, error); + + if (!dir) + { + g_slist_free_full(dirs_to_enum, g_free); + g_slist_free_full(entries, g_free); + return NULL; + } + + while ((dir_entry = g_dir_read_name(dir))) + { + gchar* full_path = + g_build_path( + G_DIR_SEPARATOR_S, + (gchar*) iter->data, + dir_entry, + NULL + ); + + // Take a copy into dirs to enum if needed + // + if (recursive && g_file_test(full_path, G_FILE_TEST_IS_DIR)) + { + WINTC_LOG_DEBUG("shcommon: enum iter, add dir %s", dir_entry); + + dirs_to_enum = + g_slist_append(dirs_to_enum, g_strdup(full_path)); + } + + // Move into entries list if it's one we're interested in + // + if (!test || g_file_test(full_path, test)) + { + entries = + g_slist_append( + entries, + g_strdup(full_names ? full_path : dir_entry) + ); + } + + g_free(full_path); + } + + g_dir_close(dir); + + // Iter to next dir + // + iter = iter->next; + } + + g_slist_free_full(dirs_to_enum, g_free); + + return entries; +} diff --git a/shared/shell/CMakeLists.txt b/shared/shell/CMakeLists.txt index 4918961..17da538 100644 --- a/shared/shell/CMakeLists.txt +++ b/shared/shell/CMakeLists.txt @@ -37,8 +37,6 @@ add_library( public/cpl.h src/error.c public/error.h - src/fs.c - src/fs.h src/icnvwbeh.c public/icnvwbeh.h public/trevwbeh.h diff --git a/shared/shell/src/cpl.c b/shared/shell/src/cpl.c index 9d83e09..054bedf 100644 --- a/shared/shell/src/cpl.c +++ b/shared/shell/src/cpl.c @@ -1,8 +1,8 @@ #include #include +#include #include "../public/cpl.h" -#include "fs.h" // // PRIVATE CONSTANTS @@ -20,7 +20,14 @@ static const gchar* S_CPL_KEY_ICON_NAME = "Icon"; // GSList* wintc_sh_cpl_applet_get_all(void) { - GSList* entries = wintc_sh_fs_get_names_as_list(S_CPL_ENTRIES_DIR, TRUE, NULL); + GSList* entries = + wintc_sh_fs_get_names_as_list( + S_CPL_ENTRIES_DIR, + TRUE, + G_FILE_TEST_IS_REGULAR, + FALSE, + NULL + ); if (!entries) { diff --git a/shared/shell/src/fs.c b/shared/shell/src/fs.c deleted file mode 100644 index 0aae3e8..0000000 --- a/shared/shell/src/fs.c +++ /dev/null @@ -1,47 +0,0 @@ -#include -#include - -#include "fs.h" - -// -// PUBLIC FUNCTIONS -// -GSList* wintc_sh_fs_get_names_as_list( - const gchar* path, - gboolean full_names, - GError** error -) -{ - GDir* dir; - const gchar* dir_entry = NULL; - GSList* entries = NULL; - - WINTC_SAFE_REF_CLEAR(error); - - WINTC_LOG_DEBUG("fs: enum dir %s", path); - - dir = g_dir_open(path, 0, error); - - if (!dir) - { - return NULL; - } - - while ((dir_entry = g_dir_read_name(dir))) - { - gchar* next_name = full_names ? - g_build_path( - G_DIR_SEPARATOR_S, - path, - dir_entry, - NULL - ) : - g_strdup(dir_entry); - - entries = g_slist_append(entries, next_name); - } - - g_dir_close(dir); - - return entries; -} diff --git a/shared/shell/src/fs.h b/shared/shell/src/fs.h deleted file mode 100644 index 675e478..0000000 --- a/shared/shell/src/fs.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef __FS_H__ -#define __FS_H__ - -#include - -// -// FIXME: Temp! fs.c/h should be moved to another lib! -// -// For now we just have sync I/O here, we need: -// - Should move to async I/O -// - Move FS stuff to a separate lib -// - MIME type detection -// - Symlink/shortcut handling -// - -// -// PUBLIC FUNCTIONS -// -GSList* wintc_sh_fs_get_names_as_list( - const gchar* path, - gboolean full_names, - GError** error -); - -#endif diff --git a/shared/shell/src/vwfs.c b/shared/shell/src/vwfs.c index 43676f3..3dafb67 100644 --- a/shared/shell/src/vwfs.c +++ b/shared/shell/src/vwfs.c @@ -3,7 +3,6 @@ #include #include "../public/vwfs.h" -#include "fs.h" // // PRIVATE ENUMS @@ -262,7 +261,14 @@ void wintc_sh_view_fs_refresh_items( // FIXME: Error handling (no way of passing to caller) // - GSList* entries = wintc_sh_fs_get_names_as_list(view_fs->path, FALSE, NULL); + GSList* entries = + wintc_sh_fs_get_names_as_list( + view_fs->path, + FALSE, + 0, + FALSE, + NULL + ); // Create actual array // FIXME: list->array should probs be in comgtk diff --git a/shell/cpl/desk/CMakeLists.txt b/shell/cpl/desk/CMakeLists.txt new file mode 100644 index 0000000..203ccca --- /dev/null +++ b/shell/cpl/desk/CMakeLists.txt @@ -0,0 +1,116 @@ +cmake_minimum_required(VERSION 3.5) + +project( + wintc-cpl-desk + VERSION 1.0 + DESCRIPTION "Windows Total Conversion Display Control Panel applet." + LANGUAGES C +) + +set(PROJECT_ANYARCH false) +set(PROJECT_FREESTATUS true) +set(PROJECT_MAINTAINER "Rory Fewell ") + +set(PROJECT_ROOT ${CMAKE_CURRENT_LIST_DIR}) + +include(GNUInstallDirs) + +include(../../../packaging/cmake-inc/common/CMakeLists.txt) +include(../../../packaging/cmake-inc/linking/CMakeLists.txt) +include(../../../packaging/cmake-inc/locale/CMakeLists.txt) +include(../../../packaging/cmake-inc/packaging/CMakeLists.txt) +include(../../../packaging/cmake-inc/resources/CMakeLists.txt) + +wintc_resolve_library(glib-2.0 GLIB) +wintc_resolve_library(gtk+-3.0 GTK3) +wintc_resolve_library(wintc-comctl WINTC_COMCTL) +wintc_resolve_library(wintc-comgtk WINTC_COMGTK) +wintc_resolve_library(wintc-registry WINTC_REGISTRY) +wintc_resolve_library(wintc-shcommon WINTC_SHCOMMON) +wintc_resolve_library(wintc-shlang WINTC_SHLANG) + +wintc_compile_resources() +wintc_create_meta_h() + +add_executable( + wintc-cpl-desk + src/application.c + src/application.h + src/main.c + src/meta.h + src/monitor.c + src/monitor.h + src/resources.c + src/settings.c + src/settings.h + src/window.c + src/window.h +) + +set_target_properties( + wintc-cpl-desk + PROPERTIES + OUTPUT_NAME desk.cpl +) + +target_compile_options( + wintc-cpl-desk + PRIVATE ${WINTC_COMPILE_OPTIONS} +) + +target_include_directories( + wintc-cpl-desk + SYSTEM + PRIVATE ${GLIB_INCLUDE_DIRS} + PRIVATE ${GTK3_INCLUDE_DIRS} + PRIVATE ${WINTC_COMCTL_INCLUDE_DIRS} + PRIVATE ${WINTC_COMGTK_INCLUDE_DIRS} + PRIVATE ${WINTC_REGISTRY_INCLUDE_DIRS} + PRIVATE ${WINTC_SHCOMMON_INCLUDE_DIRS} + PRIVATE ${WINTC_SHLANG_INCLUDE_DIRS} +) + +target_link_directories( + wintc-cpl-desk + PRIVATE ${GLIB_LIBRARY_DIRS} + PRIVATE ${GTK3_LIBRARY_DIRS} + PRIVATE ${WINTC_COMCTL_LIBRARY_DIRS} + PRIVATE ${WINTC_COMGTK_LIBRARY_DIRS} + PRIVATE ${WINTC_REGISTRY_LIBRARY_DIRS} + PRIVATE ${WINTC_SHCOMMON_LIBRARY_DIRS} + PRIVATE ${WINTC_SHLANG_LIBRARY_DIRS} +) + +target_link_libraries( + wintc-cpl-desk + PRIVATE m + PRIVATE ${GLIB_LIBRARIES} + PRIVATE ${GTK3_LIBRARIES} + PRIVATE ${WINTC_COMCTL_LIBRARIES} + PRIVATE ${WINTC_COMGTK_LIBRARIES} + PRIVATE ${WINTC_REGISTRY_LIBRARIES} + PRIVATE ${WINTC_SHCOMMON_LIBRARIES} + PRIVATE ${WINTC_SHLANG_LIBRARIES} +) + +add_dependencies( + wintc-cpl-desk + build-gresources +) + +# Installation +# +wintc_configure_and_install_packaging() + +install( + FILES desk.desktop + DESTINATION share/wintc/cpl +) +install( + FILES uk.co.oddmatics.wintc.cpl-desk.desktop + DESTINATION share/applications +) +install( + TARGETS wintc-cpl-desk + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) diff --git a/shell/cpl/desk/deps b/shell/cpl/desk/deps new file mode 100644 index 0000000..61b8603 --- /dev/null +++ b/shell/cpl/desk/deps @@ -0,0 +1,7 @@ +bt,rt:glib2 +bt,rt:gtk3 +bt,rt:wintc-comctl +bt,rt:wintc-comgtk +bt,rt:wintc-registry +bt,rt:wintc-shcommon +bt,rt:wintc-shlang diff --git a/shell/cpl/desk/desk.desktop b/shell/cpl/desk/desk.desktop new file mode 100644 index 0000000..786ed47 --- /dev/null +++ b/shell/cpl/desk/desk.desktop @@ -0,0 +1,5 @@ +[CplApplet] +Name=Display +Comment=Change the appearance of your desktop, such as the background, screen saver, colors, font sizes, and screen resolution. +Exec=desk.cpl +Icon=preferences-desktop-display diff --git a/shell/cpl/desk/src/application.c b/shell/cpl/desk/src/application.c new file mode 100644 index 0000000..bea1775 --- /dev/null +++ b/shell/cpl/desk/src/application.c @@ -0,0 +1,97 @@ +#include +#include +#include +#include + +#include "application.h" +#include "window.h" + +// +// CONSTANTS +// +static const gchar* S_APP_ID = "uk.co.oddmatics.wintc.cpl-desk"; + +// +// GTK OOP CLASS/INSTANCE DEFINITIONS +// +struct _WinTCCplDeskApplicationClass +{ + GtkApplicationClass __parent__; +}; + +struct _WinTCCplDeskApplication +{ + GtkApplication __parent__; +}; + +// +// FORWARD DECLARATIONS +// +static void wintc_cpl_desk_application_activate( + GApplication* application +); +static void wintc_cpl_desk_application_startup( + GApplication* application +); + +// +// GTK TYPE DEFINITIONS & CTORS +// +G_DEFINE_TYPE( + WinTCCplDeskApplication, + wintc_cpl_desk_application, + GTK_TYPE_APPLICATION +) + +static void wintc_cpl_desk_application_class_init( + WinTCCplDeskApplicationClass* klass +) +{ + GApplicationClass* application_class = G_APPLICATION_CLASS(klass); + + application_class->activate = wintc_cpl_desk_application_activate; + application_class->startup = wintc_cpl_desk_application_startup; +} + +static void wintc_cpl_desk_application_init( + WINTC_UNUSED(WinTCCplDeskApplication* self) +) { } + +// +// CLASS VIRTUAL METHODS +// +static void wintc_cpl_desk_application_activate( + GApplication* application +) +{ + GtkWidget* new_window = + wintc_cpl_desk_window_new(WINTC_CPL_DESK_APPLICATION(application)); + + gtk_widget_show_all(new_window); +} + +static void wintc_cpl_desk_application_startup( + GApplication* application +) +{ + (G_APPLICATION_CLASS(wintc_cpl_desk_application_parent_class)) + ->startup(application); + + g_set_prgname(S_APP_ID); + + wintc_ctl_install_default_styles(); +} + +// +// PUBLIC FUNCTIONS +// +WinTCCplDeskApplication* wintc_cpl_desk_application_new(void) +{ + return WINTC_CPL_DESK_APPLICATION( + g_object_new( + wintc_cpl_desk_application_get_type(), + "application-id", S_APP_ID, + NULL + ) + ); +} diff --git a/shell/cpl/desk/src/application.h b/shell/cpl/desk/src/application.h new file mode 100644 index 0000000..0934eae --- /dev/null +++ b/shell/cpl/desk/src/application.h @@ -0,0 +1,27 @@ +#ifndef __APPLICATION_H__ +#define __APPLICATION_H__ + +#include +#include + +// +// GTK OOP BOILERPLATE +// +typedef struct _WinTCCplDeskApplicationClass WinTCCplDeskApplicationClass; +typedef struct _WinTCCplDeskApplication WinTCCplDeskApplication; + +#define WINTC_TYPE_CPL_DESK_APPLICATION (wintc_cpl_desk_application_get_type()) +#define WINTC_CPL_DESK_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), WINTC_TYPE_CPL_DESK_APPLICATION, WinTCCplDeskApplication)) +#define WINTC_CPL_DESK_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WINTC_TYPE_CPL_DESK_APPLICATION, WinTCCplDeskApplicationClass)) +#define IS_WINTC_CPL_DESK_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), WINTC_TYPE_CPL_DESK_APPLICATION)) +#define IS_WINTC_CPL_DESK_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), WINTC_TYPE_CPL_DESK_APPLICATION)) +#define WINTC_CPL_DESK_APPLICATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), WINTC_TYPE_CPL_DESK_APPLICATION, WinTCCplDeskApplicationClass)) + +GType wintc_cpl_desk_application_get_type(void) G_GNUC_CONST; + +// +// PUBLIC FUNCTIONS +// +WinTCCplDeskApplication* wintc_cpl_desk_application_new(void); + +#endif diff --git a/shell/cpl/desk/src/main.c b/shell/cpl/desk/src/main.c new file mode 100644 index 0000000..96f167d --- /dev/null +++ b/shell/cpl/desk/src/main.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include + +#include "application.h" +#include "meta.h" + +int main( + int argc, + char* argv[] +) +{ + // Set up locales + // + setlocale(LC_ALL, ""); + bindtextdomain(PKG_NAME, WINTC_LOCALE_DIR); + textdomain(PKG_NAME); + + // Launch application + // + WinTCCplDeskApplication* app = wintc_cpl_desk_application_new(); + int status; + + g_set_application_name("Display"); + + status = g_application_run(G_APPLICATION(app), argc, argv); + + g_object_unref(app); + + return status; +} diff --git a/shell/cpl/desk/src/monitor.c b/shell/cpl/desk/src/monitor.c new file mode 100644 index 0000000..b805140 --- /dev/null +++ b/shell/cpl/desk/src/monitor.c @@ -0,0 +1,303 @@ +#include +#include +#include +#include +#include + +#include "monitor.h" + +// +// STATIC CONSTANTS +// +static const gint S_PREVIEW_IMAGE_OFFSET_X = 16; +static const gint S_PREVIEW_IMAGE_OFFSET_Y = 17; + +static const gint S_PREVIEW_IMAGE_WIDTH = 152; +static const gint S_PREVIEW_IMAGE_HEIGHT = 112; + +// +// GTK OOP CLASS/INSTANCE DEFINITIONS +// +struct _WinTCDeskMonitorClass +{ + GtkWidgetClass __parent__; + + GdkPixbuf* pixbuf_monitor; + cairo_surface_t* surface_monitor; + + gint pixbuf_monitor_height; + gint pixbuf_monitor_width; +}; + +struct _WinTCDeskMonitor +{ + GtkWidget __parent__; + + // UI + // + GdkPixbuf* pixbuf_preview; + cairo_surface_t* surface_preview; +}; + +// +// FORWARD DECLARATIONS +// +static void wintc_desk_monitor_dispose( + GObject* object +); + +static gboolean wintc_desk_monitor_draw( + GtkWidget* widget, + cairo_t* cr +); +static void wintc_desk_monitor_get_preferred_height( + GtkWidget* widget, + gint* minimum_height, + gint* natural_height +); +static void wintc_desk_monitor_get_preferred_height_for_width( + GtkWidget* widget, + gint width, + gint* minimum_height, + gint* natural_height +); +static void wintc_desk_monitor_get_preferred_width( + GtkWidget* widget, + gint* minimum_width, + gint* natural_width +); +static void wintc_desk_monitor_get_preferred_width_for_height( + GtkWidget* widget, + gint height, + gint* minimum_width, + gint* natural_width +); + +// +// GTK TYPE DEFINITIONS & CTORS +// +G_DEFINE_TYPE( + WinTCDeskMonitor, + wintc_desk_monitor, + GTK_TYPE_WIDGET +) + +static void wintc_desk_monitor_class_init( + WinTCDeskMonitorClass* klass +) +{ + GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); + GObjectClass* object_class = G_OBJECT_CLASS(klass); + + object_class->dispose = wintc_desk_monitor_dispose; + + widget_class->draw = + wintc_desk_monitor_draw; + widget_class->get_preferred_height = + wintc_desk_monitor_get_preferred_height; + widget_class->get_preferred_height_for_width = + wintc_desk_monitor_get_preferred_height_for_width; + widget_class->get_preferred_width = + wintc_desk_monitor_get_preferred_width; + widget_class->get_preferred_width_for_height = + wintc_desk_monitor_get_preferred_width_for_height; + + // Load shared monitor graphic resource + // + GError* error = NULL; + + klass->pixbuf_monitor = + gdk_pixbuf_new_from_resource( + "/uk/oddmatics/wintc/cpl-desk/monitor.png", + &error + ); + + wintc_log_error_and_clear(&error); + + if (klass->pixbuf_monitor) + { + klass->surface_monitor = + gdk_cairo_surface_create_from_pixbuf( + klass->pixbuf_monitor, + 1, + NULL + ); + + klass->pixbuf_monitor_height = + gdk_pixbuf_get_height( + klass->pixbuf_monitor + ); + klass->pixbuf_monitor_width = + gdk_pixbuf_get_width( + klass->pixbuf_monitor + ); + } +} + +static void wintc_desk_monitor_init( + WinTCDeskMonitor* self +) +{ + gtk_widget_set_has_window(GTK_WIDGET(self), FALSE); +} + +// +// CLASS VIRTUAL METHODS +// +static void wintc_desk_monitor_dispose( + GObject* object +) +{ + WinTCDeskMonitor* monitor = WINTC_DESK_MONITOR(object); + + g_clear_object(&(monitor->pixbuf_preview)); + cairo_surface_destroy(g_steal_pointer(&monitor->surface_preview)); +} + +static gboolean wintc_desk_monitor_draw( + GtkWidget* widget, + cairo_t* cr +) +{ + WinTCDeskMonitorClass* klass = WINTC_DESK_MONITOR_GET_CLASS(widget); + WinTCDeskMonitor* monitor = WINTC_DESK_MONITOR(widget); + + // monitor size + gint monitor_height = klass->pixbuf_monitor_height; + gint monitor_width = klass->pixbuf_monitor_width; + + // my size + gint my_height = gtk_widget_get_allocated_height(widget); + gint my_width = gtk_widget_get_allocated_width(widget); + + // drawing origin + gdouble target_x = floor((my_width / 2.0f) - (monitor_width / 2.0f)); + gdouble target_y = floor((my_height / 2.0f) - (monitor_height / 2.0f)); + + cairo_translate( + cr, + target_x, + target_y + ); + cairo_set_source_surface( + cr, + klass->surface_monitor, + 0.0f, + 0.0f + ); + cairo_pattern_set_extend( + cairo_get_source(cr), + CAIRO_EXTEND_NONE + ); + cairo_paint(cr); + + if (monitor->surface_preview) + { + cairo_translate( + cr, + S_PREVIEW_IMAGE_OFFSET_X, + S_PREVIEW_IMAGE_OFFSET_Y + ); + cairo_set_source_surface( + cr, + monitor->surface_preview, + 0.0f, + 0.0f + ); + cairo_pattern_set_extend( + cairo_get_source(cr), + CAIRO_EXTEND_NONE + ); + cairo_paint(cr); + } + + return FALSE; +} + +static void wintc_desk_monitor_get_preferred_height( + GtkWidget* widget, + gint* minimum_height, + gint* natural_height +) +{ + WinTCDeskMonitorClass* klass = WINTC_DESK_MONITOR_GET_CLASS(widget); + + *minimum_height = klass->pixbuf_monitor_height; + *natural_height = klass->pixbuf_monitor_height; +} + +static void wintc_desk_monitor_get_preferred_height_for_width( + GtkWidget* widget, + WINTC_UNUSED(gint width), + gint* minimum_height, + gint* natural_height +) +{ + wintc_desk_monitor_get_preferred_height( + widget, + minimum_height, + natural_height + ); +} + +static void wintc_desk_monitor_get_preferred_width( + GtkWidget* widget, + gint* minimum_width, + gint* natural_width +) +{ + WinTCDeskMonitorClass* klass = WINTC_DESK_MONITOR_GET_CLASS(widget); + + *minimum_width = klass->pixbuf_monitor_width; + *natural_width = klass->pixbuf_monitor_width; +} + +static void wintc_desk_monitor_get_preferred_width_for_height( + GtkWidget* widget, + WINTC_UNUSED(gint height), + gint* minimum_width, + gint* natural_width +) +{ + wintc_desk_monitor_get_preferred_width( + widget, + minimum_width, + natural_width + ); +} + +// +// PUBLIC FUNCTIONS +// +GtkWidget* wintc_desk_monitor_new(void) +{ + return GTK_WIDGET( + g_object_new( + WINTC_TYPE_DESK_MONITOR, + NULL + ) + ); +} + +void wintc_desk_monitor_set_pixbuf( + WinTCDeskMonitor* monitor, + const GdkPixbuf* pixbuf +) +{ + g_clear_object(&(monitor->pixbuf_preview)); + cairo_surface_destroy(g_steal_pointer(&monitor->surface_preview)); + + monitor->pixbuf_preview = + gdk_pixbuf_scale_simple( + pixbuf, + S_PREVIEW_IMAGE_WIDTH, + S_PREVIEW_IMAGE_HEIGHT, + GDK_INTERP_NEAREST + ); + monitor->surface_preview = + gdk_cairo_surface_create_from_pixbuf( + monitor->pixbuf_preview, + 1, + NULL + ); +} diff --git a/shell/cpl/desk/src/monitor.h b/shell/cpl/desk/src/monitor.h new file mode 100644 index 0000000..4163c18 --- /dev/null +++ b/shell/cpl/desk/src/monitor.h @@ -0,0 +1,33 @@ +#ifndef __MONITOR_H__ +#define __MONITOR_H__ + +#include +#include +#include + +// +// GTK OOP BOILERPLATE +// +typedef struct _WinTCDeskMonitorClass WinTCDeskMonitorClass; +typedef struct _WinTCDeskMonitor WinTCDeskMonitor; + +#define WINTC_TYPE_DESK_MONITOR (wintc_desk_monitor_get_type()) +#define WINTC_DESK_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), WINTC_TYPE_DESK_MONITOR, WinTCDeskMonitor)) +#define WINTC_DESK_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WINTC_TYPE_DESK_MONITOR, WinTCDeskMonitorClass)) +#define IS_WINTC_DESK_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), WINTC_TYPE_DESK_MONITOR)) +#define IS_WINTC_DESK_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), WINTC_TYPE_DESK_MONITOR)) +#define WINTC_DESK_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), WINTC_TYPE_DESK_MONITOR, WinTCDeskMonitorClass)) + +GType wintc_desk_monitor_get_type(void) G_GNUC_CONST; + +// +// PUBLIC FUNCTIONS +// +GtkWidget* wintc_desk_monitor_new(void); + +void wintc_desk_monitor_set_pixbuf( + WinTCDeskMonitor* monitor, + const GdkPixbuf* pixbuf +); + +#endif diff --git a/shell/cpl/desk/src/res/desk.ui b/shell/cpl/desk/src/res/desk.ui new file mode 100644 index 0000000..a87cf70 --- /dev/null +++ b/shell/cpl/desk/src/res/desk.ui @@ -0,0 +1,80 @@ + + + + + + True + False + vertical + + + True + True + + + True + True + 0 + + + + + True + False + + + Apply + True + True + True + win.apply + + + False + True + end + 0 + + + + + %CTL_CANCEL% + True + True + True + win.cancel + + + False + True + end + 1 + + + + + %CTL_OK% + True + True + True + win.ok + + + False + True + end + 2 + + + + + + False + True + 1 + + + + diff --git a/shell/cpl/desk/src/res/monitor.png b/shell/cpl/desk/src/res/monitor.png new file mode 100644 index 0000000..692e1cc Binary files /dev/null and b/shell/cpl/desk/src/res/monitor.png differ diff --git a/shell/cpl/desk/src/res/page-appearance.ui b/shell/cpl/desk/src/res/page-appearance.ui new file mode 100644 index 0000000..f8cb39b --- /dev/null +++ b/shell/cpl/desk/src/res/page-appearance.ui @@ -0,0 +1,164 @@ + + + + + True + True + Appearance + + + True + False + vertical + + + + + + True + False + horizontal + + + + + True + False + vertical + + + + True + False + Windows and buttons: + 0.0 + + + False + False + 0 + + + + + True + True + + + False + False + 1 + + + + + + True + False + Color scheme: + 0.0 + + + False + False + 2 + + + + + True + True + + + False + False + 3 + + + + + + True + False + Font size: + 0.0 + + + False + False + 4 + + + + + True + True + + + False + False + 5 + + + + + True + True + 0 + + + + + + + True + False + vertical + 2 + + + + True + True + True + 2 + Effects... + False + + + False + False + 0 + + + + + True + True + True + 2 + Advanced + False + + + False + False + 1 + + + + + True + True + 1 + + + + + False + False + 0 + + + + diff --git a/shell/cpl/desk/src/res/page-desktop.ui b/shell/cpl/desk/src/res/page-desktop.ui new file mode 100644 index 0000000..a062534 --- /dev/null +++ b/shell/cpl/desk/src/res/page-desktop.ui @@ -0,0 +1,183 @@ + + + + + True + True + Desktop + + + True + False + vertical + + + + True + False + + + True + True + 0 + + + + + + True + False + Background: + 0.0 + + + False + False + 1 + + + + + + + True + False + horizontal + + + + + True + False + vertical + + + + True + False + False + 0 + + + + True + True + + + + + True + True + 0 + + + + + + True + True + True + Customize Desktop... + False + + + False + False + 1 + + + + + True + True + 0 + + + + + + + True + False + vertical + + + + True + True + True + %PUNC_MOREINPUT%%CTL_BROWSE% + False + + + False + False + 0 + + + + + + True + False + Position: + 0.0 + + + False + False + 1 + + + + + True + True + + + False + False + 2 + + + + + + True + False + Color: + 0.0 + + + False + False + 3 + + + + + True + True + + + False + False + 4 + + + + + False + False + 1 + + + + + True + True + 2 + + + + diff --git a/shell/cpl/desk/src/res/page-scrnsave.ui b/shell/cpl/desk/src/res/page-scrnsave.ui new file mode 100644 index 0000000..cae5cba --- /dev/null +++ b/shell/cpl/desk/src/res/page-scrnsave.ui @@ -0,0 +1,219 @@ + + + + + True + True + Screen Saver + + + True + False + vertical + + + + True + False + + + True + True + 0 + + + + + + + True + False + Screen saver + + + + True + False + vertical + + + + True + False + horizontal + + + + True + True + False + + + True + True + 0 + + + + + True + True + True + Settings + False + + + False + False + 1 + + + + + True + True + True + Preview + False + + + False + False + 2 + + + + + False + False + 0 + + + + + + True + False + horizontal + + + + True + False + Wait: + 0.0 + + + False + False + 0 + + + + + True + False + False + + + False + False + 1 + + + + + True + False + minutes + 0.0 + + + False + False + 2 + + + + + True + False + On resume, display Welcome screen + False + + + False + False + 3 + + + + + False + False + 1 + + + + + + + False + False + 1 + + + + + True + False + Monitor power + + + + True + False + vertical + + + + True + False + To adjust monitor power settings and save energy, click Power. + 49 + True + 1.0 + + + False + False + 0 + + + + + + True + True + True + 2 + Power + False + + + False + False + 1 + + + + + + + False + False + 2 + + + + diff --git a/shell/cpl/desk/src/res/page-settings.ui b/shell/cpl/desk/src/res/page-settings.ui new file mode 100644 index 0000000..5f5a590 --- /dev/null +++ b/shell/cpl/desk/src/res/page-settings.ui @@ -0,0 +1,187 @@ + + + + + True + True + Settings + + + True + False + vertical + + + + + True + False + + + True + True + 0 + + + + + + True + False + Display: + 0.0 + + + False + False + 1 + + + + + True + False + (not implemented) + 0.0 + + + False + False + 2 + + + + + True + False + True + horizontal + + + + + True + False + Screen resolution + + + + True + False + vertical + + + + True + False + horizontal + + + + True + False + Less + 0.0 + + + False + False + 0 + + + + + True + False + + + True + True + 1 + + + + + True + False + More + 0.0 + + + False + False + 2 + + + + + False + False + 0 + + + + + True + False + blam! pixels + + + False + False + 1 + + + + + + + True + True + 0 + + + + + + True + False + Color quality + + + + True + False + vertical + + + + True + True + + + False + False + 0 + + + + + + + + + True + True + 1 + + + + + False + False + 3 + + + + diff --git a/shell/cpl/desk/src/res/page-themes.ui b/shell/cpl/desk/src/res/page-themes.ui new file mode 100644 index 0000000..0d87044 --- /dev/null +++ b/shell/cpl/desk/src/res/page-themes.ui @@ -0,0 +1,113 @@ + + + + + True + True + Themes + + + True + False + vertical + + + + True + False + A theme is a background plus a set of sounds, icons, and other elements to help you personalize your computer with one click. + 71 + True + 0.0 + + + False + False + 0 + + + + + + + True + False + Theme: + 0.0 + + + False + False + 1 + + + + + + True + False + horizontal + + + + + + True + True + 0 + + + + + + %CTL_SAVEAS% + True + True + True + False + + + False + False + 1 + + + + + + %CTL_DELETE% + True + True + True + False + + + False + False + 2 + + + + + False + False + 2 + + + + + + + True + False + Sample: + 0.0 + + + False + False + 3 + + + + diff --git a/shell/cpl/desk/src/res/resources.xml b/shell/cpl/desk/src/res/resources.xml new file mode 100644 index 0000000..8722a88 --- /dev/null +++ b/shell/cpl/desk/src/res/resources.xml @@ -0,0 +1,13 @@ + + + + desk.ui + page-appearance.ui + page-desktop.ui + page-scrnsave.ui + page-settings.ui + page-themes.ui + monitor.png + sample.png + + diff --git a/shell/cpl/desk/src/res/sample.png b/shell/cpl/desk/src/res/sample.png new file mode 100644 index 0000000..d7ba071 Binary files /dev/null and b/shell/cpl/desk/src/res/sample.png differ diff --git a/shell/cpl/desk/src/settings.c b/shell/cpl/desk/src/settings.c new file mode 100644 index 0000000..14baa1b --- /dev/null +++ b/shell/cpl/desk/src/settings.c @@ -0,0 +1,246 @@ +#include +#include +#include + +#include "settings.h" + +// +// PRIVATE ENUMS +// +enum +{ + PROP_SETTINGS_CHANGED = 1 +}; + +// +// GTK OOP CLASS/INSTANCE DEFINITIONS +// +struct _WinTCCplDeskSettingsClass +{ + GObjectClass __parent__; +}; + +struct _WinTCCplDeskSettings +{ + GObject __parent__; + + // Properties + // + gchar* wallpaper_path; + + // State + // + gboolean changed; + WinTCRegistry* registry; +}; + +// +// FORWARD DELCARATIONS +// +static void wintc_cpl_desk_settings_get_property( + GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec +); +static void wintc_cpl_desk_settings_set_property( + GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec +); + +static void wintc_cpl_desk_settings_bump_changed( + WinTCCplDeskSettings* settings +); + +// +// GTK TYPE DEFINITIONS & CTORS +// +G_DEFINE_TYPE( + WinTCCplDeskSettings, + wintc_cpl_desk_settings, + G_TYPE_OBJECT +) + +static void wintc_cpl_desk_settings_class_init( + WinTCCplDeskSettingsClass* klass +) +{ + GObjectClass* object_class = G_OBJECT_CLASS(klass); + + object_class->get_property = wintc_cpl_desk_settings_get_property; + object_class->set_property = wintc_cpl_desk_settings_set_property; + + g_object_class_install_property( + object_class, + PROP_SETTINGS_CHANGED, + g_param_spec_boolean( + "settings-changed", + "SettingsChanged", + "The state of whether there are unsaved changes to settings.", + FALSE, + G_PARAM_READWRITE + ) + ); +} + +static void wintc_cpl_desk_settings_init( + WinTCCplDeskSettings* self +) +{ + self->registry = wintc_registry_new(); +} + +// +// CLASS VIRTUAL METHODS +// +static void wintc_cpl_desk_settings_get_property( + GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec +) +{ + WinTCCplDeskSettings* settings = WINTC_CPL_DESK_SETTINGS(object); + + switch (prop_id) + { + case PROP_SETTINGS_CHANGED: + g_value_set_boolean(value, settings->changed); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void wintc_cpl_desk_settings_set_property( + GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec +) +{ + WinTCCplDeskSettings* settings = WINTC_CPL_DESK_SETTINGS(object); + + switch (prop_id) + { + case PROP_SETTINGS_CHANGED: + settings->changed = g_value_get_boolean(value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +// +// PUBLIC FUNCTIONS +// +WinTCCplDeskSettings* wintc_cpl_desk_settings_new(void) +{ + return g_object_new(WINTC_TYPE_CPL_DESK_SETTINGS, NULL); +} + +gboolean wintc_cpl_desk_settings_apply( + WinTCCplDeskSettings* settings, + GError** error +) +{ + if ( + !wintc_registry_set_key_value( + settings->registry, + "HKCU\\Control Panel\\Desktop", + "Wallpaper", + WINTC_REG_SZ, + &(settings->wallpaper_path), + FALSE, + error + ) + ) + { + return FALSE; + } + + g_object_set( + settings, + "settings-changed", FALSE, + NULL + ); + + return TRUE; +} + +gboolean wintc_cpl_desk_settings_load( + WinTCCplDeskSettings* settings, + GError** error +) +{ + gchar* sz_wallpaper = NULL; + + if ( + !wintc_registry_get_key_value( + settings->registry, + "HKCU\\Control Panel\\Desktop", + "Wallpaper", + WINTC_REG_SZ, + &sz_wallpaper, + error + ) + ) + { + return FALSE; + } + + // Bin old settings + // + g_free(g_steal_pointer(&(settings->wallpaper_path))); + + // Load in new settings + // + settings->wallpaper_path = sz_wallpaper; + + g_object_set( + settings, + "settings-changed", FALSE, + NULL + ); + + return TRUE; +} + +const gchar* wintc_cpl_desk_settings_get_wallpaper( + WinTCCplDeskSettings* settings +) +{ + return settings->wallpaper_path; +} + +void wintc_cpl_desk_settings_set_wallpaper( + WinTCCplDeskSettings* settings, + const gchar* path +) +{ + g_free(g_steal_pointer(&(settings->wallpaper_path))); + + settings->wallpaper_path = g_strdup(path); + + wintc_cpl_desk_settings_bump_changed(settings); +} + +// +// PRIVATE FUNCTIONS +// +static void wintc_cpl_desk_settings_bump_changed( + WinTCCplDeskSettings* settings +) +{ + g_object_set( + settings, + "settings-changed", TRUE, + NULL + ); +} diff --git a/shell/cpl/desk/src/settings.h b/shell/cpl/desk/src/settings.h new file mode 100644 index 0000000..e85607a --- /dev/null +++ b/shell/cpl/desk/src/settings.h @@ -0,0 +1,43 @@ +#ifndef __SETTINGS_H__ +#define __SETTINGS_H__ + +#include + +// +// GTK OOP BOILERPLATE +// +typedef struct _WinTCCplDeskSettingsClass WinTCCplDeskSettingsClass; +typedef struct _WinTCCplDeskSettings WinTCCplDeskSettings; + +#define WINTC_TYPE_CPL_DESK_SETTINGS (wintc_cpl_desk_settings_get_type()) +#define WINTC_CPL_DESK_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), WINTC_TYPE_CPL_DESK_SETTINGS, WinTCCplDeskSettings)) +#define WINTC_CPL_DESK_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WINTC_TYPE_CPL_DESK_SETTINGS, WinTCCplDeskSettingsClass)) +#define IS_WINTC_CPL_DESK_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), WINTC_TYPE_CPL_DESK_SETTINGS)) +#define IS_WINTC_CPL_DESK_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), WINTC_TYPE_CPL_DESK_SETTINGS)) +#define WINTC_CPL_DESK_SETTINGS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), WINTC_TYPE_CPL_DESK_SETTINGS, WinTCCplDeskSettingsClass)) + +GType wintc_cpl_desk_settings_get_type(void) G_GNUC_CONST; + +// +// PUBLIC FUNCTIONS +// +WinTCCplDeskSettings* wintc_cpl_desk_settings_new(void); + +gboolean wintc_cpl_desk_settings_apply( + WinTCCplDeskSettings* settings, + GError** error +); +gboolean wintc_cpl_desk_settings_load( + WinTCCplDeskSettings* settings, + GError** error +); + +const gchar* wintc_cpl_desk_settings_get_wallpaper( + WinTCCplDeskSettings* settings +); +void wintc_cpl_desk_settings_set_wallpaper( + WinTCCplDeskSettings* settings, + const gchar* path +); + +#endif diff --git a/shell/cpl/desk/src/window.c b/shell/cpl/desk/src/window.c new file mode 100644 index 0000000..8073ead --- /dev/null +++ b/shell/cpl/desk/src/window.c @@ -0,0 +1,520 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "application.h" +#include "monitor.h" +#include "settings.h" +#include "window.h" + +// +// FORWARD DECLARATIONS +// +static void wintc_cpl_desk_window_finalize( + GObject* object +); + +static void add_wallpaper_to_list( + WinTCCplDeskWindow* wnd, + const gchar* path +); +static void refresh_wallpaper_list( + WinTCCplDeskWindow* wnd +); +static void select_wallpaper_from_list( + WinTCCplDeskWindow* wnd, + const gchar* path +); + +static void action_apply( + GSimpleAction* action, + GVariant* parameter, + gpointer user_data +); +static void action_cancel( + GSimpleAction* action, + GVariant* parameter, + gpointer user_data +); +static void action_ok( + GSimpleAction* action, + GVariant* parameter, + gpointer user_data +); + +static void on_listbox_wallpapers_row_selected( + GtkListBox* self, + GtkListBoxRow* row, + gpointer user_data +); +static gboolean on_window_map_event( + GtkWidget* self, + GdkEventAny* event, + gpointer user_data +); + +// +// STATIC DATA +// +static GActionEntry s_window_actions[] = { + { + .name = "apply", + .activate = action_apply, + .parameter_type = NULL, + .state = NULL, + .change_state = NULL + }, + { + .name = "cancel", + .activate = action_cancel, + .parameter_type = NULL, + .state = NULL, + .change_state = NULL + }, + { + .name = "ok", + .activate = action_ok, + .parameter_type = NULL, + .state = NULL, + .change_state = NULL + } +}; + +// +// GTK OOP CLASS/INSTANCE DEFINITIONS +// +struct _WinTCCplDeskWindowClass +{ + GtkApplicationWindowClass __parent__; +}; + +struct _WinTCCplDeskWindow +{ + GtkApplicationWindow __parent__; + + // State + // + GSList* list_wallpapers; + WinTCCplDeskSettings* settings; + + // UI + // + GtkWidget* monitor_desktop; + GtkWidget* listbox_wallpapers; + GtkWidget* notebook_main; + + gboolean sync_settings; +}; + +// +// GTK TYPE DEFINITION & CTORS +// +G_DEFINE_TYPE( + WinTCCplDeskWindow, + wintc_cpl_desk_window, + GTK_TYPE_APPLICATION_WINDOW +) + +static void wintc_cpl_desk_window_class_init( + WinTCCplDeskWindowClass* klass +) +{ + GObjectClass* object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = wintc_cpl_desk_window_finalize; +} + +static void wintc_cpl_desk_window_init( + WinTCCplDeskWindow* self +) +{ + GtkBuilder* builder; + GtkWidget* main_box; + + gtk_widget_set_size_request( + GTK_WIDGET(self), + 414, // Approx. + 454 + ); + gtk_window_set_default_size( + GTK_WINDOW(self), + 414, // Approx. + 454 + ); + gtk_window_set_icon_name( + GTK_WINDOW(self), + "preferences-desktop-display" + ); + gtk_window_set_resizable( + GTK_WINDOW(self), + FALSE + ); + gtk_window_set_skip_taskbar_hint( + GTK_WINDOW(self), + TRUE + ); + + // Init settings + // + self->settings = wintc_cpl_desk_settings_new(); + + // Define GActions + // + g_action_map_add_action_entries( + G_ACTION_MAP(self), + s_window_actions, + G_N_ELEMENTS(s_window_actions), + self + ); + + g_object_bind_property( + self->settings, + "settings-changed", + g_action_map_lookup_action( + G_ACTION_MAP(self), + "apply" + ), + "enabled", + 0 + ); + + // Initialize UI + // + g_type_ensure(WINTC_TYPE_DESK_MONITOR); + + builder = + gtk_builder_new_from_resource( + "/uk/oddmatics/wintc/cpl-desk/desk.ui" + ); + + wintc_lc_builder_preprocess_widget_text(builder); + + main_box = GTK_WIDGET(gtk_builder_get_object(builder, "main-box")); + + gtk_container_add(GTK_CONTAINER(self), main_box); + + // Pull out widgets + // + self->notebook_main = GTK_WIDGET( + gtk_builder_get_object( + builder, + "main-notebook" + ) + ); + + g_object_unref(G_OBJECT(builder)); + + // Init pages + // + wintc_ctl_cpl_notebook_append_page_from_resource( + GTK_NOTEBOOK(self->notebook_main), + "/uk/oddmatics/wintc/cpl-desk/page-themes.ui", + NULL + ); + wintc_ctl_cpl_notebook_append_page_from_resource( + GTK_NOTEBOOK(self->notebook_main), + "/uk/oddmatics/wintc/cpl-desk/page-desktop.ui", + "listbox-wallpapers", &(self->listbox_wallpapers), + "monitor", &(self->monitor_desktop), + NULL + ); + wintc_ctl_cpl_notebook_append_page_from_resource( + GTK_NOTEBOOK(self->notebook_main), + "/uk/oddmatics/wintc/cpl-desk/page-scrnsave.ui", + NULL + ); + wintc_ctl_cpl_notebook_append_page_from_resource( + GTK_NOTEBOOK(self->notebook_main), + "/uk/oddmatics/wintc/cpl-desk/page-appearance.ui", + NULL + ); + wintc_ctl_cpl_notebook_append_page_from_resource( + GTK_NOTEBOOK(self->notebook_main), + "/uk/oddmatics/wintc/cpl-desk/page-settings.ui", + NULL + ); + + // Attach signals + // + g_signal_connect( + self, + "map-event", + G_CALLBACK(on_window_map_event), + NULL + ); + + g_signal_connect( + self->listbox_wallpapers, + "row-selected", + G_CALLBACK(on_listbox_wallpapers_row_selected), + self + ); + + // Initial load stuff + // + refresh_wallpaper_list(self); +} + +// +// CLASS VIRTUAL METHODS +// +static void wintc_cpl_desk_window_finalize( + GObject* object +) +{ + WinTCCplDeskWindow* wnd = WINTC_CPL_DESK_WINDOW(object); + + g_slist_free_full(wnd->list_wallpapers, g_free); + + (G_OBJECT_CLASS(wintc_cpl_desk_window_parent_class))->finalize(object); +} + +// +// PUBLIC FUNCTIONS +// +GtkWidget* wintc_cpl_desk_window_new( + WinTCCplDeskApplication* app +) +{ + return GTK_WIDGET( + g_object_new( + WINTC_TYPE_CPL_DESK_WINDOW, + "application", GTK_APPLICATION(app), + "title", _("Display Properties"), + NULL + ) + ); +} + +// +// PRIVATE FUNCTIONS +// +static void add_wallpaper_to_list( + WinTCCplDeskWindow* wnd, + const gchar* path +) +{ + gchar* filename = g_path_get_basename(path); + GtkWidget* label = gtk_label_new(filename); + + gtk_label_set_xalign(GTK_LABEL(label), 0.0f); + + gtk_list_box_insert( + GTK_LIST_BOX(wnd->listbox_wallpapers), + label, + -1 + ); + + g_free(filename); +} + +static void refresh_wallpaper_list( + WinTCCplDeskWindow* wnd +) +{ + g_slist_free_full(wnd->list_wallpapers, g_free); + wintc_container_clear(GTK_CONTAINER(wnd->listbox_wallpapers)); + + // Load up wallpapers + // + GError* error = NULL; + GSList* iter = NULL; + + wnd->list_wallpapers = + wintc_sh_fs_get_names_as_list( + WINTC_RT_PREFIX "/share/backgrounds", + TRUE, + G_FILE_TEST_IS_REGULAR, + TRUE, + &error + ); + + if (!wnd->list_wallpapers) + { + wintc_nice_error_and_clear(&error); + return; + } + + iter = wnd->list_wallpapers; + + while (iter) + { + add_wallpaper_to_list(wnd, (gchar*) iter->data); + iter = iter->next; + } +} + +static void select_wallpaper_from_list( + WinTCCplDeskWindow* wnd, + const gchar* path +) +{ + gint i = 0; + + if (!path) + { + return; + } + + for (GSList* iter = wnd->list_wallpapers; iter; iter = iter->next) + { + if (g_strcmp0((gchar*) iter->data, path) == 0) + { + gtk_list_box_select_row( + GTK_LIST_BOX(wnd->listbox_wallpapers), + gtk_list_box_get_row_at_index( + GTK_LIST_BOX(wnd->listbox_wallpapers), + i + ) + ); + return; + } + + i++; + } + + // The path isnt in the listbox, so add it and select last item + // + add_wallpaper_to_list(wnd, path); + + gtk_list_box_select_row( + GTK_LIST_BOX(wnd->listbox_wallpapers), + gtk_list_box_get_row_at_index( + GTK_LIST_BOX(wnd->listbox_wallpapers), + g_slist_length(wnd->list_wallpapers) - 1 + ) + ); +} + +// +// CALLBACKS +// +static void action_apply( + WINTC_UNUSED(GSimpleAction* action), + WINTC_UNUSED(GVariant* parameter), + gpointer user_data +) +{ + WinTCCplDeskWindow* wnd = WINTC_CPL_DESK_WINDOW(user_data); + + GError* error = NULL; + + if (!wintc_cpl_desk_settings_apply(wnd->settings, &error)) + { + wintc_nice_error_and_clear(&error); + } +} + +static void action_cancel( + WINTC_UNUSED(GSimpleAction* action), + WINTC_UNUSED(GVariant* parameter), + gpointer user_data +) +{ + gtk_window_close(GTK_WINDOW(user_data)); +} + +static void action_ok( + WINTC_UNUSED(GSimpleAction* action), + WINTC_UNUSED(GVariant* parameter), + gpointer user_data +) +{ + GAction* action_apply = + g_action_map_lookup_action( + G_ACTION_MAP(user_data), + "apply" + ); + + // Try applying settings... + // + g_action_activate( + action_apply, + NULL + ); + + // ...we can assume it worked if the apply action is disabled + // + if (!g_action_get_enabled(action_apply)) + { + gtk_window_close(GTK_WINDOW(user_data)); + } +} + +static void on_listbox_wallpapers_row_selected( + WINTC_UNUSED(GtkListBox* self), + GtkListBoxRow* row, + gpointer user_data +) +{ + WinTCCplDeskWindow* wnd = WINTC_CPL_DESK_WINDOW(user_data); + + GError* error = NULL; + GdkPixbuf* pixbuf_wallpaper; + const gchar* wallpaper_path; + + wallpaper_path = + g_slist_nth_data( + wnd->list_wallpapers, + gtk_list_box_row_get_index(row) + ); + + pixbuf_wallpaper = + gdk_pixbuf_new_from_file( + wallpaper_path, + &error + ); + + if (!pixbuf_wallpaper) + { + wintc_nice_error_and_clear(&error); + return; + } + + wintc_desk_monitor_set_pixbuf( + WINTC_DESK_MONITOR(wnd->monitor_desktop), + pixbuf_wallpaper + ); + + if (!wnd->sync_settings) + { + wintc_cpl_desk_settings_set_wallpaper( + wnd->settings, + wallpaper_path + ); + } + + g_object_unref(pixbuf_wallpaper); +} + +static gboolean on_window_map_event( + GtkWidget* self, + WINTC_UNUSED(GdkEventAny* event), + WINTC_UNUSED(gpointer user_data) +) +{ + WinTCCplDeskWindow* wnd = WINTC_CPL_DESK_WINDOW(self); + + GError* error = NULL; + + if (!wintc_cpl_desk_settings_load(wnd->settings, &error)) + { + wintc_nice_error_and_clear(&error); + return FALSE; + } + + wnd->sync_settings = TRUE; + + select_wallpaper_from_list( + wnd, + wintc_cpl_desk_settings_get_wallpaper(wnd->settings) + ); + + wnd->sync_settings = FALSE; + + return FALSE; +} diff --git a/shell/cpl/desk/src/window.h b/shell/cpl/desk/src/window.h new file mode 100644 index 0000000..b822a6f --- /dev/null +++ b/shell/cpl/desk/src/window.h @@ -0,0 +1,31 @@ +#ifndef __WINDOW_H__ +#define __WINDOW_H__ + +#include +#include + +#include "application.h" + +// +// GTK OOP BOILERPLATE +// +typedef struct _WinTCCplDeskWindowClass WinTCCplDeskWindowClass; +typedef struct _WinTCCplDeskWindow WinTCCplDeskWindow; + +#define WINTC_TYPE_CPL_DESK_WINDOW (wintc_cpl_desk_window_get_type()) +#define WINTC_CPL_DESK_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), WINTC_TYPE_CPL_DESK_WINDOW, WinTCCplDeskWindow)) +#define WINTC_CPL_DESK_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WINTC_TYPE_CPL_DESK_WINDOW, WinTCCplDeskWindowClass)) +#define IS_WINTC_CPL_DESK_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), WINTC_TYPE_CPL_DESK_WINDOW)) +#define IS_WINTC_CPL_DESK_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), WINTC_TYPE_CPL_DESK_WINDOW)) +#define WINTC_CPL_DESK_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), WINTC_TYPE_CPL_DESK_WINDOW, WinTCCplDeskWindowClass)) + +GType wintc_cpl_desk_window_get_type(void) G_GNUC_CONST; + +// +// PUBLIC FUNCTIONS +// +GtkWidget* wintc_cpl_desk_window_new( + WinTCCplDeskApplication* app +); + +#endif diff --git a/shell/cpl/desk/uk.co.oddmatics.wintc.cpl-desk.desktop b/shell/cpl/desk/uk.co.oddmatics.wintc.cpl-desk.desktop new file mode 100644 index 0000000..2829416 --- /dev/null +++ b/shell/cpl/desk/uk.co.oddmatics.wintc.cpl-desk.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Name=Display +Comment=Change the appearance of your desktop, such as the background, screen saver, colors, font sizes, and screen resolution. +Exec=desk.cpl +Icon=preferences-desktop-display +Terminal=false +StartupNotify=false +Type=Application +Categories=Settings;GTK; +OnlyShowIn=WINTC; diff --git a/shell/cpl/sysdm/src/window.c b/shell/cpl/sysdm/src/window.c index d6f58d2..6a97919 100644 --- a/shell/cpl/sysdm/src/window.c +++ b/shell/cpl/sysdm/src/window.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -10,11 +11,6 @@ // // FORWARD DECLARATIONS // -static void add_sysdm_page( - GtkNotebook* notebook_main, - gchar* resource_path -); - static void action_close( GSimpleAction* action, GVariant* parameter, @@ -117,9 +113,10 @@ static void wintc_cpl_sysdm_window_init( // Init pages // - add_sysdm_page( + wintc_ctl_cpl_notebook_append_page_from_resource( GTK_NOTEBOOK(self->notebook_main), - "/uk/oddmatics/wintc/cpl-sysdm/page-gen.ui" + "/uk/oddmatics/wintc/cpl-sysdm/page-gen.ui", + NULL ); } @@ -140,34 +137,6 @@ GtkWidget* wintc_cpl_sysdm_window_new( ); } -// -// PRIVATE FUNCTIONS -// -static void add_sysdm_page( - GtkNotebook* notebook_main, - gchar* resource_path -) -{ - GtkBuilder* builder; - GtkWidget* box_page; - GtkWidget* label_title; - - builder = gtk_builder_new_from_resource(resource_path); - - wintc_lc_builder_preprocess_widget_text(builder); - - box_page = GTK_WIDGET(gtk_builder_get_object(builder, "page-box")); - label_title = GTK_WIDGET(gtk_builder_get_object(builder, "label-title")); - - gtk_notebook_append_page( - notebook_main, - box_page, - label_title - ); - - g_object_unref(G_OBJECT(builder)); -} - // // CALLBACKS // diff --git a/shell/desktop/CMakeLists.txt b/shell/desktop/CMakeLists.txt index 83554df..3e87702 100644 --- a/shell/desktop/CMakeLists.txt +++ b/shell/desktop/CMakeLists.txt @@ -22,6 +22,7 @@ include(../../packaging/cmake-inc/packaging/CMakeLists.txt) wintc_resolve_library(glib-2.0 GLIB) wintc_resolve_library(gtk+-3.0 GTK3) wintc_resolve_library(wintc-comgtk WINTC_COMGTK) +wintc_resolve_library(wintc-registry WINTC_REGISTRY) wintc_resolve_library(wintc-shelldpa WINTC_SHELLDPA) add_executable( @@ -29,6 +30,8 @@ add_executable( src/application.c src/application.h src/main.c + src/settings.c + src/settings.h src/window.c src/window.h ) @@ -45,6 +48,7 @@ target_include_directories( PRIVATE ${GLIB_INCLUDE_DIRS} PRIVATE ${GTK3_INCLUDE_DIRS} PRIVATE ${WINTC_COMGTK_INCLUDE_DIRS} + PRIVATE ${WINTC_REGISTRY_INCLUDE_DIRS} PRIVATE ${WINTC_SHELLDPA_INCLUDE_DIRS} ) @@ -53,6 +57,7 @@ target_link_directories( PRIVATE ${GLIB_LIBRARY_DIRS} PRIVATE ${GTK3_LIBRARY_DIRS} PRIVATE ${WINTC_COMGTK_LIBRARY_DIRS} + PRIVATE ${WINTC_REGISTRY_LIBRARY_DIRS} PRIVATE ${WINTC_SHELLDPA_LIBRARY_DIRS} ) @@ -61,6 +66,7 @@ target_link_libraries( PRIVATE ${GLIB_LIBRARIES} PRIVATE ${GTK3_LIBRARIES} PRIVATE ${WINTC_COMGTK_LIBRARIES} + PRIVATE ${WINTC_REGISTRY_LIBRARIES} PRIVATE ${WINTC_SHELLDPA_LIBRARIES} ) diff --git a/shell/desktop/deps b/shell/desktop/deps index 7b8e468..edf3c06 100644 --- a/shell/desktop/deps +++ b/shell/desktop/deps @@ -1,4 +1,5 @@ bt,rt:glib2 bt,rt:gtk3 bt,rt:wintc-comgtk +bt,rt:wintc-registry bt,rt:wintc-shelldpa diff --git a/shell/desktop/src/application.c b/shell/desktop/src/application.c index fccfccf..5d034b9 100644 --- a/shell/desktop/src/application.c +++ b/shell/desktop/src/application.c @@ -4,6 +4,7 @@ #include #include "application.h" +#include "settings.h" #include "window.h" // @@ -17,6 +18,8 @@ struct _WinTCDesktopApplicationClass struct _WinTCDesktopApplication { GtkApplication __parent__; + + WinTCDesktopSettings* settings; }; // @@ -49,8 +52,11 @@ static void wintc_desktop_application_class_init( } static void wintc_desktop_application_init( - WINTC_UNUSED(WinTCDesktopApplication* self) -) {} + WinTCDesktopApplication* self +) +{ + self->settings = wintc_desktop_settings_new(); +} // // PUBLIC FUNCTIONS @@ -97,7 +103,11 @@ static void wintc_desktop_application_activate( for (int i = 0; i < n_monitors; i++) { GdkMonitor* monitor = gdk_display_get_monitor(display, i); - GtkWidget* wnd = wintc_desktop_window_new(desktop_app, monitor); + GtkWidget* wnd = wintc_desktop_window_new( + desktop_app, + monitor, + desktop_app->settings + ); gtk_widget_show_all(wnd); } diff --git a/shell/desktop/src/settings.c b/shell/desktop/src/settings.c new file mode 100644 index 0000000..bf855d8 --- /dev/null +++ b/shell/desktop/src/settings.c @@ -0,0 +1,283 @@ +#include +#include +#include +#include + +#include "settings.h" + +// +// PRIVATE ENUMS +// +enum +{ + PROP_PIXBUF_WALLPAPER = 1 +}; + +// +// GTK OOP CLASS/INSTANCE DEFINITIONS +// +struct _WinTCDesktopSettingsClass +{ + GObjectClass __parent__; +}; + +struct _WinTCDesktopSettings +{ + GObject __parent__; + + // State + // + WinTCRegistry* registry; + + // Properties + // + GdkPixbuf* pixbuf_wallpaper; +}; + +// +// FORWARD DECLARATIONS +// +static void wintc_desktop_settings_dispose( + GObject* object +); +static void wintc_desktop_settings_get_property( + GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec +); +static void wintc_desktop_settings_set_property( + GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec +); + +static void wintc_desktop_settings_set_wallpaper_path( + WinTCDesktopSettings* settings, + const gchar* wallpaper_path +); + +static void regkey_changed_desktop_cb( + WinTCRegistry* registry, + const gchar* key_path, + const gchar* value_name, + GVariant* value_variant, + gpointer user_data +); + +// +// GTK TYPE DEFINITIONS & CTORS +// +G_DEFINE_TYPE( + WinTCDesktopSettings, + wintc_desktop_settings, + G_TYPE_OBJECT +) + +static void wintc_desktop_settings_class_init( + WinTCDesktopSettingsClass* klass +) +{ + GObjectClass* object_class = G_OBJECT_CLASS(klass); + + object_class->dispose = wintc_desktop_settings_dispose; + object_class->get_property = wintc_desktop_settings_get_property; + object_class->set_property = wintc_desktop_settings_set_property; + + g_object_class_install_property( + object_class, + PROP_PIXBUF_WALLPAPER, + g_param_spec_object( + "pixbuf-wallpaper", + "PixbufWallpaper", + "The pixbuf that should be used as the desktop wallpaper.", + GDK_TYPE_PIXBUF, + G_PARAM_READWRITE + ) + ); +} + +static void wintc_desktop_settings_init( + WinTCDesktopSettings* self +) +{ + GError* error = NULL; + + // Initial registry set up + // + self->registry = wintc_registry_new(); + + if ( + !wintc_registry_create_key( + self->registry, + "HKCU\\Control Panel\\Desktop", + &error + ) + ) + { + wintc_log_error_and_clear(&error); + } + + // Get some stuff from registry for startup + // + gchar* sz_wallpaper = NULL; + + if ( + !wintc_registry_get_key_value( + self->registry, + "HKCU\\Control Panel\\Desktop", + "Wallpaper", + WINTC_REG_SZ, + &sz_wallpaper, + &error + ) + ) + { + wintc_log_error_and_clear(&error); + } + + if (sz_wallpaper) + { + wintc_desktop_settings_set_wallpaper_path(self, sz_wallpaper); + g_free(sz_wallpaper); + } + + // Set up watcher + // + wintc_registry_watch_key( + self->registry, + "HKCU\\Control Panel\\Desktop", + regkey_changed_desktop_cb, + self + ); +} + +// +// CLASS VIRTUAL METHODS +// +static void wintc_desktop_settings_dispose( + GObject* object +) +{ + WinTCDesktopSettings* settings = WINTC_DESKTOP_SETTINGS(object); + + g_clear_object(&(settings->pixbuf_wallpaper)); + + (G_OBJECT_CLASS(wintc_desktop_settings_parent_class))->dispose(object); +} + +static void wintc_desktop_settings_get_property( + GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec +) +{ + WinTCDesktopSettings* settings = WINTC_DESKTOP_SETTINGS(object); + + switch (prop_id) + { + case PROP_PIXBUF_WALLPAPER: + g_value_set_object(value, settings->pixbuf_wallpaper); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void wintc_desktop_settings_set_property( + GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec +) +{ + WinTCDesktopSettings* settings = WINTC_DESKTOP_SETTINGS(object); + + switch (prop_id) + { + case PROP_PIXBUF_WALLPAPER: + g_clear_object(&(settings->pixbuf_wallpaper)); + settings->pixbuf_wallpaper = g_value_dup_object(value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +// +// PUBLIC FUNCTIONS +// +WinTCDesktopSettings* wintc_desktop_settings_new(void) +{ + return g_object_new(WINTC_TYPE_DESKTOP_SETTINGS, NULL); +} + +// +// PRIVATE FUNCTIONS +// +static void wintc_desktop_settings_set_wallpaper_path( + WinTCDesktopSettings* settings, + const gchar* wallpaper_path +) +{ + GError* error = NULL; + GdkPixbuf* pixbuf_wallpaper; + + WINTC_LOG_DEBUG("desktop: setting wallpaper to %s", wallpaper_path); + + pixbuf_wallpaper = + gdk_pixbuf_new_from_file( + wallpaper_path, + &error + ); + + if (!pixbuf_wallpaper) + { + wintc_log_error_and_clear(&error); + return; + } + + g_object_set( + settings, + "pixbuf-wallpaper", pixbuf_wallpaper, + NULL + ); + + g_object_unref(pixbuf_wallpaper); +} + +// +// CALLBACK +// +static void regkey_changed_desktop_cb( + WINTC_UNUSED(WinTCRegistry* registry), + const gchar* key_path, + const gchar* value_name, + GVariant* value_variant, + WINTC_UNUSED(gpointer user_data) +) +{ + WinTCDesktopSettings* settings = WINTC_DESKTOP_SETTINGS(user_data); + + WINTC_LOG_DEBUG("desktop: saw key changed %s->%s", key_path, value_name); + + if (g_strcmp0(value_name, "Wallpaper") == 0) + { + if (wintc_registry_get_type_for_variant(value_variant) != WINTC_REG_SZ) + { + WINTC_LOG_DEBUG("%s", "desktop: wallpaper setting is not REG_SZ"); + return; + } + + wintc_desktop_settings_set_wallpaper_path( + settings, + g_variant_get_string(value_variant, NULL) + ); + } +} diff --git a/shell/desktop/src/settings.h b/shell/desktop/src/settings.h new file mode 100644 index 0000000..6440073 --- /dev/null +++ b/shell/desktop/src/settings.h @@ -0,0 +1,26 @@ +#ifndef __SETTINGS_H__ +#define __SETTINGS_H__ + +#include + +// +// GTK OOP BOILERPLATE +// +typedef struct _WinTCDesktopSettingsClass WinTCDesktopSettingsClass; +typedef struct _WinTCDesktopSettings WinTCDesktopSettings; + +#define WINTC_TYPE_DESKTOP_SETTINGS (wintc_desktop_settings_get_type()) +#define WINTC_DESKTOP_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), WINTC_TYPE_DESKTOP_SETTINGS, WinTCDesktopSettings)) +#define WINTC_DESKTOP_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WINTC_TYPE_DESKTOP_SETTINGS, WinTCDesktopSettingsClass)) +#define IS_WINTC_DESKTOP_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), WINTC_TYPE_DESKTOP_SETTINGS)) +#define IS_WINTC_DESKTOP_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), WINTC_TYPE_DESKTOP_SETTINGS)) +#define WINTC_DESKTOP_SETTINGS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), WINTC_TYPE_DESKTOP_SETTINGS, WinTCDesktopSettingsClass)) + +GType wintc_desktop_settings_get_type(void) G_GNUC_CONST; + +// +// PUBLIC FUNCTIONS +// +WinTCDesktopSettings* wintc_desktop_settings_new(void); + +#endif diff --git a/shell/desktop/src/window.c b/shell/desktop/src/window.c index 7045199..a1f63a4 100644 --- a/shell/desktop/src/window.c +++ b/shell/desktop/src/window.c @@ -1,12 +1,22 @@ #include +#include #include #include #include #include #include "application.h" +#include "settings.h" #include "window.h" +// +// PRIVATE ENUMS +// +enum +{ + PROP_SETTINGS = 1 +}; + // // GTK OOP CLASS/INSTANCE DEFINITIONS // @@ -18,16 +28,51 @@ struct _WinTCDesktopWindowClass struct _WinTCDesktopWindow { WinTCDpaDesktopWindow __parent__; + + // Drawing-related + // + GdkPixbuf* pixbuf_wallpaper; + cairo_surface_t* surface_wallpaper; + + // State + // + WinTCDesktopSettings* settings; }; // -// FORWARD DECLARATION +// FORWARD DECLARATIONS // +static void wintc_desktop_window_dispose( + GObject* object +); +static void wintc_desktop_window_set_property( + GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec +); + static gboolean wintc_desktop_window_draw( GtkWidget* widget, cairo_t* cr ); +static void wintc_desktop_window_update_wallpaper( + WinTCDesktopWindow* wnd +); + +static gboolean on_window_map_event( + GtkWidget* self, + GdkEventAny* event, + gpointer user_data +); + +static void on_settings_notify_pixbuf_wallpaper( + GObject* self, + GParamSpec* pspec, + gpointer user_data +); + // // GTK TYPE DEFINITION & CTORS // @@ -41,28 +86,175 @@ static void wintc_desktop_window_class_init( WinTCDesktopWindowClass* klass ) { + GObjectClass* object_class = G_OBJECT_CLASS(klass); GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); + object_class->dispose = wintc_desktop_window_dispose; + object_class->set_property = wintc_desktop_window_set_property; + widget_class->draw = wintc_desktop_window_draw; + + g_object_class_install_property( + object_class, + PROP_SETTINGS, + g_param_spec_object( + "settings", + "Settings", + "The shared desktop settings", + WINTC_TYPE_DESKTOP_SETTINGS, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY + ) + ); } static void wintc_desktop_window_init( - WINTC_UNUSED(WinTCDesktopWindow* self) -) {} + WinTCDesktopWindow* self +) +{ + g_signal_connect( + self, + "map-event", + G_CALLBACK(on_window_map_event), + NULL + ); +} // // CLASS VIRTUAL METHODS // -static gboolean wintc_desktop_window_draw( - WINTC_UNUSED(GtkWidget* widget), - cairo_t* cr +static void wintc_desktop_window_dispose( + GObject* object ) { + WinTCDesktopWindow* wnd = WINTC_DESKTOP_WINDOW(object); + + if (wnd->surface_wallpaper) + { + cairo_surface_destroy(g_steal_pointer(&(wnd->surface_wallpaper))); + g_clear_object(&(wnd->pixbuf_wallpaper)); + } + + (G_OBJECT_CLASS(wintc_desktop_window_parent_class))->dispose(object); +} + +static void wintc_desktop_window_set_property( + GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec +) +{ + WinTCDesktopWindow* wnd = WINTC_DESKTOP_WINDOW(object); + + switch (prop_id) + { + case PROP_SETTINGS: + wnd->settings = g_value_get_object(value); + + g_signal_connect( + wnd->settings, + "notify::pixbuf-wallpaper", + G_CALLBACK(on_settings_notify_pixbuf_wallpaper), + wnd + ); + + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static gboolean wintc_desktop_window_draw( + GtkWidget* widget, + cairo_t* cr +) +{ + WinTCDpaDesktopWindow* dpa_wnd = WINTC_DPA_DESKTOP_WINDOW(widget); + WinTCDesktopWindow* wnd = WINTC_DESKTOP_WINDOW(widget); + // FIXME: Just drawing default desktop background colour atm // cairo_set_source_rgb(cr, 0.0f, 0.298f, 0.596f); cairo_paint(cr); + // FIXME: Billy basic drawing of the wallpaper, if present + // + if (wnd->surface_wallpaper) + { + cairo_set_source_surface(cr, wnd->surface_wallpaper, 0.0f, 0.0f); + cairo_paint(cr); + } + + // Rough watermark drawing + // + if (gdk_monitor_is_primary(dpa_wnd->monitor)) + { + static gchar* s_tag = NULL; + + cairo_text_extents_t extents; + gdouble y; + + gint height = gtk_widget_get_allocated_height(widget); + gint width = gtk_widget_get_allocated_width(widget); + + if (!s_tag) + { + gchar* tag = wintc_build_get_tag(); + + s_tag = + g_strdup_printf("For testing purposes only. Build %s", tag); + + g_free(tag); + } + + // Default to white, IDK if Windows changes this... + // + cairo_set_source_rgb(cr, 1.0f, 1.0f, 1.0f); + + // Handle build string first... + // + cairo_select_font_face( + cr, + "sans-serif", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_NORMAL + ); + + cairo_text_extents(cr, s_tag, &extents); + + cairo_move_to( + cr, + width - extents.width - 10, + height - extents.height - 34 // FIXME: Hardcoded taskband height + ); + + cairo_show_text(cr, s_tag); + + // ...and the product name + // FIXME: Hard coded temp name in here until we have a name + // + cairo_select_font_face( + cr, + "sans-serif", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_BOLD + ); + + cairo_text_extents(cr, "Windows 'Total Conversion'", &extents); + + cairo_get_current_point(cr, NULL, &y); + + cairo_move_to( + cr, + width - extents.width - 10, + y - extents.height - 4 + ); + + cairo_show_text(cr, "Windows 'Total Conversion'"); + } + return FALSE; } @@ -71,7 +263,8 @@ static gboolean wintc_desktop_window_draw( // GtkWidget* wintc_desktop_window_new( WinTCDesktopApplication* app, - GdkMonitor* monitor + GdkMonitor* monitor, + WinTCDesktopSettings* settings ) { return GTK_WIDGET( @@ -82,7 +275,69 @@ GtkWidget* wintc_desktop_window_new( "decorated", TRUE, "monitor", monitor, "resizable", FALSE, + "settings", settings, NULL ) ); } + +// +// PRIVATE FUNCTIONS +// +static void wintc_desktop_window_update_wallpaper( + WinTCDesktopWindow* wnd +) +{ + WINTC_LOG_DEBUG("%s", "desktop: window updating wallpaper"); + + if (wnd->surface_wallpaper) + { + cairo_surface_destroy(g_steal_pointer(&(wnd->surface_wallpaper))); + g_clear_object(&(wnd->pixbuf_wallpaper)); + } + + g_object_get( + wnd->settings, + "pixbuf-wallpaper", &(wnd->pixbuf_wallpaper), + NULL + ); + + if (wnd->pixbuf_wallpaper) + { + wnd->surface_wallpaper = + gdk_cairo_surface_create_from_pixbuf( + wnd->pixbuf_wallpaper, + 1, + NULL + ); + + gtk_widget_queue_draw(GTK_WIDGET(wnd)); + } +} + +// +// CALLBACKS +// +static gboolean on_window_map_event( + GtkWidget* self, + WINTC_UNUSED(GdkEventAny* event), + WINTC_UNUSED(gpointer user_data) +) +{ + wintc_desktop_window_update_wallpaper( + WINTC_DESKTOP_WINDOW(self) + ); + + return FALSE; +} + +static void on_settings_notify_pixbuf_wallpaper( + WINTC_UNUSED(GObject* self), + WINTC_UNUSED(GParamSpec* pspec), + gpointer user_data +) +{ + wintc_desktop_window_update_wallpaper( + WINTC_DESKTOP_WINDOW(user_data) + ); +} diff --git a/shell/desktop/src/window.h b/shell/desktop/src/window.h index cb661ce..6df1d07 100644 --- a/shell/desktop/src/window.h +++ b/shell/desktop/src/window.h @@ -5,6 +5,7 @@ #include #include "application.h" +#include "settings.h" // // GTK OOP BOILERPLATE @@ -26,7 +27,8 @@ GType wintc_desktop_window_get_type(void) G_GNUC_CONST; // GtkWidget* wintc_desktop_window_new( WinTCDesktopApplication* app, - GdkMonitor* monitor + GdkMonitor* monitor, + WinTCDesktopSettings* settings ); #endif diff --git a/tools/bldutils/depmap/apk-maps b/tools/bldutils/depmap/apk-maps index 6838e9f..17667dd 100644 --- a/tools/bldutils/depmap/apk-maps +++ b/tools/bldutils/depmap/apk-maps @@ -14,13 +14,17 @@ msgfmt-->bt,rt-->gettext plymouth-->bt,rt-->plymouth pulseaudio-->bt-->pulseaudio-dev pulseaudio-->rt-->libpulse +python3-packaging-->bt,rt-->py3-packaging python3-venv-->bt,rt-->py3-virtualenv sass-->bt,rt-->sassc +sqlite3-->bt-->sqlite-dev +sqlite3-->rt-->sqlite sysinfo-->bt,rt-->NULL wintc-comctl-->bt,rt-->libwintc-comctl wintc-comgtk-->bt,rt-->libwintc-comgtk wintc-exec-->bt,rt-->libwintc-exec wintc-msgina-->bt,rt-->libwintc-msgina +wintc-registry-->bt,rt-->libwintc-registry wintc-shcommon-->bt,rt-->libwintc-shcommon wintc-shell-->bt,rt-->libwintc-shell wintc-shelldpa-->bt,rt-->libwintc-shelldpa diff --git a/tools/bldutils/depmap/archpkg-maps b/tools/bldutils/depmap/archpkg-maps index e0f76fa..f442847 100644 --- a/tools/bldutils/depmap/archpkg-maps +++ b/tools/bldutils/depmap/archpkg-maps @@ -7,13 +7,16 @@ lightdm-->bt,rt-->lightdm msgfmt-->bt,rt-->gettext plymouth-->bt,rt-->plymouth pulseaudio-->bt,rt-->libpulse +python3-packaging-->bt,rt-->python-packaging python3-venv-->bt,rt-->python3 sass-->bt,rt-->ruby-sass +sqlite3-->bt,rt-->sqlite sysinfo-->bt,rt-->NULL wintc-comctl-->bt,rt-->wintc-comctl wintc-comgtk-->bt,rt-->wintc-comgtk wintc-exec-->bt,rt-->wintc-exec wintc-msgina-->bt,rt-->wintc-msgina +wintc-registry-->bt,rt-->wintc-registry wintc-shcommon-->bt,rt-->wintc-shcommon wintc-shell-->bt,rt-->wintc-shell wintc-shelldpa-->bt,rt-->wintc-shelldpa diff --git a/tools/bldutils/depmap/bsdpkg-maps b/tools/bldutils/depmap/bsdpkg-maps index d6ef7ba..75bf879 100644 --- a/tools/bldutils/depmap/bsdpkg-maps +++ b/tools/bldutils/depmap/bsdpkg-maps @@ -7,13 +7,16 @@ lightdm-->bt,rt-->lightdm msgfmt-->bt,rt-->gettext plymouth-->bt,rt-->splash pulseaudio-->bt,rt-->pulseaudio +python3-packaging-->bt,rt-->py39-packaging python3-venv-->bt,rt-->py39-virtualenv sass-->bt,rt-->rubygem-sass +sqlite3-->bt,rt-->sqlite3 sysinfo-->bt,rt-->libsysinfo wintc-comctl-->bt,rt-->wintc-comctl wintc-comgtk-->bt,rt-->wintc-comgtk wintc-exec-->bt,rt-->wintc-exec wintc-msgina-->bt,rt-->wintc-msgina +wintc-registry-->bt,rt-->wintc-registry wintc-shcommon-->bt,rt-->wintc-shcommon wintc-shell-->bt,rt-->wintc-shell wintc-shelldpa-->bt,rt-->wintc-shelldpa diff --git a/tools/bldutils/depmap/deb-maps b/tools/bldutils/depmap/deb-maps index aa5a4d7..750e3ff 100644 --- a/tools/bldutils/depmap/deb-maps +++ b/tools/bldutils/depmap/deb-maps @@ -14,13 +14,17 @@ msgfmt-->bt,rt-->gettext plymouth-->bt,rt-->plymouth pulseaudio-->bt-->libpulse-dev pulseaudio-->rt-->libpulse0 +python3-packaging-->bt,rt-->python3-packaging python3-venv-->bt,rt-->python3-venv sass-->bt,rt-->ruby-sass +sqlite3-->bt-->libsqlite3-dev +sqlite3-->rt-->libsqlite3-0 sysinfo-->bt,rt-->NULL wintc-comctl-->bt,rt-->libwintc-comctl wintc-comgtk-->bt,rt-->libwintc-comgtk wintc-exec-->bt,rt-->libwintc-exec wintc-msgina-->bt,rt-->libwintc-msgina +wintc-registry-->bt,rt-->libwintc-registry wintc-shcommon-->bt,rt-->libwintc-shcommon wintc-shell-->bt,rt-->libwintc-shell wintc-shelldpa-->bt,rt-->libwintc-shelldpa diff --git a/tools/bldutils/depmap/rpm-maps b/tools/bldutils/depmap/rpm-maps index 069d684..4417bb5 100644 --- a/tools/bldutils/depmap/rpm-maps +++ b/tools/bldutils/depmap/rpm-maps @@ -14,13 +14,17 @@ msgfmt-->bt,rt-->gettext plymouth-->bt,rt-->plymouth pulseaudio-->bt-->pulseaudio-libs-devel pulseaudio-->rt-->pulseaudio-libs +python3-packaging-->bt,rt-->python3-packaging python3-venv-->bt,rt-->python3-virtualenv sass-->bt,rt-->rubygem-sass +sqlite3-->bt-->sqlite-devel +sqlite3-->rt-->sqlite sysinfo-->bt,rt-->NULL wintc-comctl-->bt,rt-->wintc-comctl wintc-comgtk-->bt,rt-->wintc-comgtk wintc-exec-->bt,rt-->wintc-exec wintc-msgina-->bt,rt-->wintc-msgina +wintc-registry-->bt,rt-->wintc-registry wintc-shcommon-->bt,rt-->wintc-shcommon wintc-shell-->bt,rt-->wintc-shell wintc-shelldpa-->bt,rt-->wintc-shelldpa