From 53443329dca28f6b24b106f7d2c7d21b811bbdd0 Mon Sep 17 00:00:00 2001 From: Rory Fewell Date: Thu, 9 May 2024 22:36:54 +0100 Subject: [PATCH] Enhancement: Fixes #291, Implement desktop wallpaper support --- base/regsvc/CMakeLists.txt | 82 ++ base/regsvc/README.MD | 8 + base/regsvc/deps | 5 + base/regsvc/src/backend.c | 713 ++++++++++++++++++ base/regsvc/src/backend.h | 29 + base/regsvc/src/main.c | 376 +++++++++ packaging/bsdpkg/pkgimpl.sh | 2 +- packaging/cmake-inc/codegen/CMakeLists.txt | 55 ++ packaging/cmake-inc/common/CMakeLists.txt | 4 +- packaging/cmake-inc/packaging/CMakeLists.txt | 4 + packaging/targets | 2 + private/play/dbus/.gitignore | 2 + private/play/dbus/clt/CMakeLists.txt | 74 ++ private/play/dbus/clt/src/main.c | 49 ++ private/play/dbus/clt/src/testreg.xml | 8 + private/play/dbus/srv/CMakeLists.txt | 75 ++ private/play/dbus/srv/deps | 3 + private/play/dbus/srv/src/main.c | 98 +++ private/play/dbus/srv/src/testreg.xml | 8 + shared/comctl/CMakeLists.txt | 10 + shared/comctl/deps | 1 + shared/comctl/public/cpl.h | 13 + shared/comctl/public/libapi.h.in | 1 + shared/comctl/src/cpl.c | 60 ++ shared/registry/.gitignore | 2 + shared/registry/CMakeLists.txt | 91 +++ shared/registry/README.MD | 5 + shared/registry/deps | 4 + shared/registry/public/libapi.h.in | 8 + shared/registry/public/regwrap.h | 80 ++ shared/registry/public/variant.h | 15 + shared/registry/src/dbus-if.xml | 30 + shared/registry/src/regwrap.c | 433 +++++++++++ shared/registry/src/variant.c | 28 + shared/shcommon/CMakeLists.txt | 2 + shared/shcommon/public/fs.h | 15 + shared/shcommon/public/libapi.h.in | 1 + shared/shcommon/src/fs.c | 93 +++ shared/shell/CMakeLists.txt | 2 - shared/shell/src/cpl.c | 11 +- shared/shell/src/fs.c | 47 -- shared/shell/src/fs.h | 25 - shared/shell/src/vwfs.c | 10 +- shell/cpl/desk/CMakeLists.txt | 116 +++ shell/cpl/desk/deps | 7 + shell/cpl/desk/desk.desktop | 5 + shell/cpl/desk/src/application.c | 97 +++ shell/cpl/desk/src/application.h | 27 + shell/cpl/desk/src/main.c | 32 + shell/cpl/desk/src/monitor.c | 303 ++++++++ shell/cpl/desk/src/monitor.h | 33 + shell/cpl/desk/src/res/desk.ui | 80 ++ shell/cpl/desk/src/res/monitor.png | Bin 0 -> 7682 bytes shell/cpl/desk/src/res/page-appearance.ui | 164 ++++ shell/cpl/desk/src/res/page-desktop.ui | 183 +++++ shell/cpl/desk/src/res/page-scrnsave.ui | 219 ++++++ shell/cpl/desk/src/res/page-settings.ui | 187 +++++ shell/cpl/desk/src/res/page-themes.ui | 113 +++ shell/cpl/desk/src/res/resources.xml | 13 + shell/cpl/desk/src/res/sample.png | Bin 0 -> 8400 bytes shell/cpl/desk/src/settings.c | 246 ++++++ shell/cpl/desk/src/settings.h | 43 ++ shell/cpl/desk/src/window.c | 520 +++++++++++++ shell/cpl/desk/src/window.h | 31 + .../uk.co.oddmatics.wintc.cpl-desk.desktop | 10 + shell/cpl/sysdm/src/window.c | 39 +- shell/desktop/CMakeLists.txt | 6 + shell/desktop/deps | 1 + shell/desktop/src/application.c | 16 +- shell/desktop/src/settings.c | 283 +++++++ shell/desktop/src/settings.h | 26 + shell/desktop/src/window.c | 269 ++++++- shell/desktop/src/window.h | 4 +- tools/bldutils/depmap/apk-maps | 4 + tools/bldutils/depmap/archpkg-maps | 3 + tools/bldutils/depmap/bsdpkg-maps | 3 + tools/bldutils/depmap/deb-maps | 4 + tools/bldutils/depmap/rpm-maps | 4 + 78 files changed, 5548 insertions(+), 127 deletions(-) create mode 100644 base/regsvc/CMakeLists.txt create mode 100644 base/regsvc/README.MD create mode 100644 base/regsvc/deps create mode 100644 base/regsvc/src/backend.c create mode 100644 base/regsvc/src/backend.h create mode 100644 base/regsvc/src/main.c create mode 100644 packaging/cmake-inc/codegen/CMakeLists.txt create mode 100644 private/play/dbus/.gitignore create mode 100644 private/play/dbus/clt/CMakeLists.txt create mode 100644 private/play/dbus/clt/src/main.c create mode 100644 private/play/dbus/clt/src/testreg.xml create mode 100644 private/play/dbus/srv/CMakeLists.txt create mode 100644 private/play/dbus/srv/deps create mode 100644 private/play/dbus/srv/src/main.c create mode 100644 private/play/dbus/srv/src/testreg.xml create mode 100644 shared/comctl/public/cpl.h create mode 100644 shared/comctl/src/cpl.c create mode 100644 shared/registry/.gitignore create mode 100644 shared/registry/CMakeLists.txt create mode 100644 shared/registry/README.MD create mode 100644 shared/registry/deps create mode 100644 shared/registry/public/libapi.h.in create mode 100644 shared/registry/public/regwrap.h create mode 100644 shared/registry/public/variant.h create mode 100644 shared/registry/src/dbus-if.xml create mode 100644 shared/registry/src/regwrap.c create mode 100644 shared/registry/src/variant.c create mode 100644 shared/shcommon/public/fs.h create mode 100644 shared/shcommon/src/fs.c delete mode 100644 shared/shell/src/fs.c delete mode 100644 shared/shell/src/fs.h create mode 100644 shell/cpl/desk/CMakeLists.txt create mode 100644 shell/cpl/desk/deps create mode 100644 shell/cpl/desk/desk.desktop create mode 100644 shell/cpl/desk/src/application.c create mode 100644 shell/cpl/desk/src/application.h create mode 100644 shell/cpl/desk/src/main.c create mode 100644 shell/cpl/desk/src/monitor.c create mode 100644 shell/cpl/desk/src/monitor.h create mode 100644 shell/cpl/desk/src/res/desk.ui create mode 100644 shell/cpl/desk/src/res/monitor.png create mode 100644 shell/cpl/desk/src/res/page-appearance.ui create mode 100644 shell/cpl/desk/src/res/page-desktop.ui create mode 100644 shell/cpl/desk/src/res/page-scrnsave.ui create mode 100644 shell/cpl/desk/src/res/page-settings.ui create mode 100644 shell/cpl/desk/src/res/page-themes.ui create mode 100644 shell/cpl/desk/src/res/resources.xml create mode 100644 shell/cpl/desk/src/res/sample.png create mode 100644 shell/cpl/desk/src/settings.c create mode 100644 shell/cpl/desk/src/settings.h create mode 100644 shell/cpl/desk/src/window.c create mode 100644 shell/cpl/desk/src/window.h create mode 100644 shell/cpl/desk/uk.co.oddmatics.wintc.cpl-desk.desktop create mode 100644 shell/desktop/src/settings.c create mode 100644 shell/desktop/src/settings.h 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 0000000000000000000000000000000000000000..692e1cc1aa582d1a7e81316528c925b410d8cf8a GIT binary patch literal 7682 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 wWul4n + + + + 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 0000000000000000000000000000000000000000..d7ba0716ba641cbcf9b461319a15cb9d3603b6a1 GIT binary patch literal 8400 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 +#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