From 78e99c4d64e7e6e2da984838b8b7dbca38b78143 Mon Sep 17 00:00:00 2001 From: Nedko Arnaudov Date: Sat, 9 Oct 2010 16:53:03 +0300 Subject: [PATCH] ladishd: Basic recent projects functionality --- daemon/main.c | 11 +- daemon/recent_projects.c | 212 +++++++++++++++++++++++++++++++++++ daemon/recent_projects.h | 39 +++++++ daemon/recent_store.c | 285 +++++++++++++++++++++++++++++++++++++++++++++++ daemon/recent_store.h | 55 +++++++++ daemon/room.c | 2 + daemon/room_load.c | 2 + daemon/save.c | 4 +- dbus_constants.h | 1 + wscript | 2 + 10 files changed, 610 insertions(+), 3 deletions(-) create mode 100644 daemon/recent_projects.c create mode 100644 daemon/recent_projects.h create mode 100644 daemon/recent_store.c create mode 100644 daemon/recent_store.h diff --git a/daemon/main.c b/daemon/main.c index d8fab925..14c1393e 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -46,6 +46,7 @@ #include "../proxies/notify_proxy.h" #include "../proxies/conf_proxy.h" #include "conf.h" +#include "recent_projects.h" bool g_quit; const char * g_dbus_unique_name; @@ -353,11 +354,16 @@ int main(int argc, char ** argv, char ** envp) goto uninit_conf; } - if (!a2j_proxy_init()) + if (!ladish_recent_projects_init()) { goto uninit_conf; } + if (!a2j_proxy_init()) + { + goto uninit_recent_projects; + } + if (!jmcore_proxy_init()) { goto uninit_a2j; @@ -392,6 +398,9 @@ uninit_jmcore: uninit_a2j: a2j_proxy_uninit(); +uninit_recent_projects: + ladish_recent_projects_uninit(); + uninit_conf: if (g_use_notify) { diff --git a/daemon/recent_projects.c b/daemon/recent_projects.c new file mode 100644 index 00000000..b3f5ea93 --- /dev/null +++ b/daemon/recent_projects.c @@ -0,0 +1,212 @@ +/* -*- Mode: C ; c-basic-offset: 2 -*- */ +/* + * LADI Session Handler (ladish) + * + * Copyright (C) 2010 Nedko Arnaudov + * + ************************************************************************** + * This file implements the recent project functionality + ************************************************************************** + * + * LADI Session Handler is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * LADI Session Handler is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LADI Session Handler. If not, see + * or write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "recent_projects.h" +#include "recent_store.h" +#include "../common/catdup.h" +#include "../dbus_constants.h" +#include "../dbus/error.h" + +#define RECENT_PROJECTS_STORE_FILE "recent_projects" +#define RECENT_PROJECTS_STORE_MAX_ITEMS 50 + +static ladish_recent_store_handle g_recent_projects_store; + +bool ladish_recent_projects_init(void) +{ + char * path; + bool ret; + + path = catdup(g_base_dir, "/" RECENT_PROJECTS_STORE_FILE); + if (path == NULL) + { + log_error("catdup() failed to compose recent projects store file path"); + return false; + } + + ret = ladish_recent_store_create(path, RECENT_PROJECTS_STORE_MAX_ITEMS, &g_recent_projects_store); + if (!ret) + { + log_error("creation of recent projects store failed"); + } + + free(path); + + return ret; +} + +void ladish_recent_projects_uninit(void) +{ + ladish_recent_store_destroy(g_recent_projects_store); +} + +void ladish_recent_project_use(const char * project_path) +{ + ladish_recent_store_use_item(g_recent_projects_store, project_path); +} + +/**********************************************************************************/ +/* D-Bus methods */ +/**********************************************************************************/ + +struct recent_projects_callback_context +{ + DBusMessageIter array_iter; + uint16_t max_items; + bool error; +}; + +#define ctx_ptr ((struct recent_projects_callback_context *)callback_context) + +bool recent_projects_callback(void * callback_context, const char * project_path) +{ + DBusMessageIter struct_iter; + DBusMessageIter dict_iter; + bool ret; + + ASSERT(ctx_ptr->max_items > 0); + + if (!dbus_message_iter_open_container(&ctx_ptr->array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter)) + { + ctx_ptr->error = true; + goto exit; + } + + if (!dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &project_path)) + { + ctx_ptr->error = true; + goto close_struct; + } + + if (!dbus_message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)) + { + ctx_ptr->error = true; + goto close_struct; + } + + /* if (!dbus_add_dict_entry_uint32(&dict_iter, "name", project_name)) */ + /* { */ + /* ctx_ptr->error = true; */ + /* goto close_dict; */ + /* } */ + + ret = true; + +/* close_dict: */ + if (!dbus_message_iter_close_container(&struct_iter, &dict_iter)) + { + ctx_ptr->error = true; + } + +close_struct: + if (!dbus_message_iter_close_container(&ctx_ptr->array_iter, &struct_iter)) + { + ctx_ptr->error = true; + } + +exit: + if (ctx_ptr->error) + { + return false; /* stop the iteration if error occurs */ + } + + ctx_ptr->max_items--; + + /* stop iteration if the requested max is reached */ + return ctx_ptr->max_items > 0; +} + +#undef ctx_ptr + +static void get(struct dbus_method_call * call_ptr) +{ + DBusMessageIter iter; + struct recent_projects_callback_context ctx; + + if (!dbus_message_get_args(call_ptr->message, &g_dbus_error, DBUS_TYPE_UINT16, &ctx.max_items, DBUS_TYPE_INVALID)) + { + lash_dbus_error(call_ptr, LASH_DBUS_ERROR_INVALID_ARGS, "Invalid arguments to method \"%s\": %s", call_ptr->method_name, g_dbus_error.message); + dbus_error_free(&g_dbus_error); + return; + } + + if (ctx.max_items == 0) + { + lash_dbus_error( + call_ptr, + LASH_DBUS_ERROR_INVALID_ARGS, + "Invalid arguments to method \"%s\": max cannot be 0", + call_ptr->method_name); + } + + call_ptr->reply = dbus_message_new_method_return(call_ptr->message); + if (call_ptr->reply == NULL) + { + goto fail; + } + + dbus_message_iter_init_append(call_ptr->reply, &iter); + + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sa{sv})", &ctx.array_iter)) + { + goto fail_unref; + } + + ctx.error = false; + ladish_recent_store_iterate_items(g_recent_projects_store, &ctx, recent_projects_callback); + if (ctx.error) + { + goto fail_unref; + } + + if (!dbus_message_iter_close_container(&iter, &ctx.array_iter)) + { + goto fail_unref; + } + + return; + +fail_unref: + dbus_message_unref(call_ptr->reply); + call_ptr->reply = NULL; + +fail: + log_error("Ran out of memory trying to construct method return"); +} + +METHOD_ARGS_BEGIN(get, "Get list of recent items") + METHOD_ARG_DESCRIBE_IN("max", "q", "Max number of items caller is interested in") + METHOD_ARG_DESCRIBE_OUT("items", "a(sa{sv})", "Item list") +METHOD_ARGS_END + +METHODS_BEGIN + METHOD_DESCRIBE(get, get) +METHODS_END + +INTERFACE_BEGIN(g_iface_recent_items, IFACE_RECENT_ITEMS) + INTERFACE_DEFAULT_HANDLER + INTERFACE_EXPOSE_METHODS +INTERFACE_END diff --git a/daemon/recent_projects.h b/daemon/recent_projects.h new file mode 100644 index 00000000..218bd44a --- /dev/null +++ b/daemon/recent_projects.h @@ -0,0 +1,39 @@ +/* -*- Mode: C ; c-basic-offset: 2 -*- */ +/* + * LADI Session Handler (ladish) + * + * Copyright (C) 2010 Nedko Arnaudov + * + ************************************************************************** + * This file contains interface to recent projects functionality + ************************************************************************** + * + * LADI Session Handler is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * LADI Session Handler is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LADI Session Handler. If not, see + * or write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef RECENT_PROJECTS_H__51B24E64_1629_43A4_B312_14E84080E68E__INCLUDED +#define RECENT_PROJECTS_H__51B24E64_1629_43A4_B312_14E84080E68E__INCLUDED + +#include "common.h" + +bool ladish_recent_projects_init(void); +void ladish_recent_projects_uninit(void); + +void ladish_recent_project_use(const char * project_path); + +extern const struct dbus_interface_descriptor g_iface_recent_items; + +#endif /* #ifndef RECENT_PROJECTS_H__51B24E64_1629_43A4_B312_14E84080E68E__INCLUDED */ diff --git a/daemon/recent_store.c b/daemon/recent_store.c new file mode 100644 index 00000000..d2a60a15 --- /dev/null +++ b/daemon/recent_store.c @@ -0,0 +1,285 @@ +/* -*- Mode: C ; c-basic-offset: 2 -*- */ +/* + * LADI Session Handler (ladish) + * + * Copyright (C) 2010 Nedko Arnaudov + * + ************************************************************************** + * This file contains implementation of the recent items store + ************************************************************************** + * + * LADI Session Handler is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * LADI Session Handler is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LADI Session Handler. If not, see + * or write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include + +#include "recent_store.h" +#include "save.h" + +struct ladish_recent_store +{ + char * path; + unsigned int max_items; + char ** items; +}; + +static +void +ladish_recent_store_save( + struct ladish_recent_store * store_ptr) +{ + unsigned int i; + int fd; + + fd = open(store_ptr->path, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd == -1) + { + log_error("open(%s) failed: %d (%s)", store_ptr->path, errno, strerror(errno)); + return; + } + + for (i = 0; i < store_ptr->max_items && store_ptr->items[i] != NULL; i++) + { + if (!ladish_write_string(fd, store_ptr->items[i])) + { + log_error("write to file '%s' failed", store_ptr->path); + break; + } + + if (!ladish_write_string(fd, "\n")) + { + log_error("write to file '%s' failed", store_ptr->path); + break; + } + } + + close(fd); +} + +static +void +ladish_recent_store_load( + struct ladish_recent_store * store_ptr) +{ + int fd; + struct stat st; + char * buffer; + size_t buffer_size; + ssize_t ret; + char * temp_ptr; + unsigned int i; + + fd = open(store_ptr->path, O_RDONLY); + if (fd == -1) + { + log_error("open(%s) failed: %d (%s)", store_ptr->path, errno, strerror(errno)); + goto exit; + } + + if (fstat(fd, &st) != 0) + { + log_error("fstat(%s) failed: %d (%s)", store_ptr->path, errno, strerror(errno)); + goto close; + } + + buffer_size = (size_t)st.st_size; + buffer = malloc(buffer_size + 1); + if (buffer == NULL) + { + log_error("malloc() failed to allocate %zy byte buffer for reading the contents of the recent items file '%s'", buffer_size + 1, store_ptr->path); + goto close; + } + + ret = read(fd, buffer, buffer_size); + if (ret == -1) + { + log_error("read(%s) failed: %d (%s)", store_ptr->path, errno, strerror(errno)); + goto free; + } + + if (ret < (size_t)buffer_size) + { + /* this could be handled better but we dont care because it is expected for the file to be small enough */ + log_error("read(%s) returned less bytes than requested", store_ptr->path); + goto free; + } + + buffer[buffer_size] = 0; + + /* read lines and store them in the item array */ + temp_ptr = strtok(buffer, "\n"); + i = 0; + while (temp_ptr != NULL && i < store_ptr->max_items) + { + ASSERT(store_ptr->items[i] == NULL); /* filling an empty array */ + + store_ptr->items[i] = strdup(temp_ptr); + if (store_ptr->items[i] == NULL) + { + log_error("strdup(\"%s\") failed.", temp_ptr); + goto free; + } + + i++; + temp_ptr = strtok(NULL, "\n"); + } + +free: + free(buffer); +close: + close(fd); +exit: + return; +} + +bool +ladish_recent_store_create( + const char * file_path, + unsigned int max_items, + ladish_recent_store_handle * store_handle_ptr) +{ + struct ladish_recent_store * store_ptr; + unsigned int i; + + store_ptr = malloc(sizeof(struct ladish_recent_store)); + if (store_ptr == NULL) + { + log_error("malloc() failed to allocate a ladish_recent_store struct"); + goto fail; + } + + store_ptr->path = strdup(file_path); + if (store_ptr->path == NULL) + { + log_error("strdup() failed for recent store path '%s'", file_path); + goto free_store; + } + + store_ptr->items = malloc(sizeof(char *) * max_items); + if (store_ptr->items == NULL) + { + log_error("malloc() failed to array of %u pointers", max_items); + goto free_path; + } + + for (i = 0; i < max_items; i++) + { + store_ptr->items[i] = NULL; + } + + store_ptr->max_items = max_items; + + /* try to load items from file */ + ladish_recent_store_load(store_ptr); + + *store_handle_ptr = (ladish_recent_store_handle)store_ptr; + + return true; + +free_path: + free(store_ptr->path); +free_store: + free(store_ptr); +fail: + return false; +} + +#define store_ptr ((struct ladish_recent_store *)store_handle) + +void +ladish_recent_store_destroy( + ladish_recent_store_handle store_handle) +{ + free(store_ptr->items); + free(store_ptr->path); + free(store_ptr); +} + +void +ladish_recent_store_use_item( + ladish_recent_store_handle store_handle, + const char * item_const) +{ + char * item; + char * item_save; + unsigned int i; + + for (i = 0; i < store_ptr->max_items && store_ptr->items[i] != NULL; i++) + { + if (strcmp(store_ptr->items[i], item_const) == 0) + { /* already known item */ + + /* if the item is already the most recent one, there is nothing to do */ + if (i != 0) + { + item = store_ptr->items[i]; + store_ptr->items[i] = NULL; /* mark end for reorder loop */ + goto reorder; + } + + return; + } + } + + /* new item */ + item = strdup(item_const); + if (item == NULL) + { + log_error("strdup() failed for recent item '%s'", item_const); + return; + } + +reorder: + /* we have valid non-NULL item pointer before the iteration begins */ + /* the item pointer is checked for NULL because that means either + end of used items or a mark created when already known item + was removed from the array */ + for (i = 0; i < store_ptr->max_items && item != NULL; i++) + { + item_save = store_ptr->items[i]; + store_ptr->items[i] = item; + item = item_save; + } + + if (item != NULL) + { /* eventually free the oldest item */ + free(item); + } + + ladish_recent_store_save(store_ptr); +} + +void +ladish_recent_store_iterate_items( + ladish_recent_store_handle store_handle, + void * callback_context, + bool (* callback)(void * callback_context, const char * item)) +{ + unsigned int i; + + for (i = 0; i < store_ptr->max_items && store_ptr->items[i] != NULL; i++) + { + if (!callback(callback_context, store_ptr->items[i])) + { + break; + } + } +} + +#undef store_ptr diff --git a/daemon/recent_store.h b/daemon/recent_store.h new file mode 100644 index 00000000..042361e6 --- /dev/null +++ b/daemon/recent_store.h @@ -0,0 +1,55 @@ +/* -*- Mode: C ; c-basic-offset: 2 -*- */ +/* + * LADI Session Handler (ladish) + * + * Copyright (C) 2010 Nedko Arnaudov + * + ************************************************************************** + * This file contains interface to recent items store + ************************************************************************** + * + * LADI Session Handler is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * LADI Session Handler is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LADI Session Handler. If not, see + * or write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef RECENT_STORE_H__52816145_014A_4CDA_B713_D7F39AAB4E73__INCLUDED +#define RECENT_STORE_H__52816145_014A_4CDA_B713_D7F39AAB4E73__INCLUDED + +#include "common.h" + +typedef struct ladish_recent_store_tag { int unused; } * ladish_recent_store_handle; + +bool +ladish_recent_store_create( + const char * file_path, + unsigned int max_items, + ladish_recent_store_handle * store_ptr); + +void +ladish_recent_store_destroy( + ladish_recent_store_handle store); + +void +ladish_recent_store_use_item( + ladish_recent_store_handle store, + const char * item); + +void +ladish_recent_store_iterate_items( + ladish_recent_store_handle store, + void * callback_context, + bool (* callback)(void * callback_context, const char * item)); + +#endif /* #ifndef RECENT_STORE_H__52816145_014A_4CDA_B713_D7F39AAB4E73__INCLUDED */ diff --git a/daemon/room.c b/daemon/room.c index a3a87074..ac633fd4 100644 --- a/daemon/room.c +++ b/daemon/room.c @@ -32,6 +32,7 @@ #include "../proxies/jmcore_proxy.h" #include "cmd.h" #include "../dbus/error.h" +#include "recent_projects.h" extern const struct dbus_interface_descriptor g_interface_room; @@ -312,6 +313,7 @@ ladish_room_create( &g_interface_patchbay, ladish_graph_get_dbus_context(room_ptr->graph), &g_iface_graph_dict, room_ptr->graph, &g_iface_app_supervisor, room_ptr->app_supervisor, + &g_iface_recent_items, NULL, NULL); if (room_ptr->dbus_object == NULL) { diff --git a/daemon/room_load.c b/daemon/room_load.c index 67517f4d..70a1acab 100644 --- a/daemon/room_load.c +++ b/daemon/room_load.c @@ -37,6 +37,7 @@ #include "../proxies/notify_proxy.h" #include "escape.h" #include "studio.h" +#include "recent_projects.h" #define context_ptr ((struct ladish_parse_context *)data) #define room_ptr ((struct ladish_room *)context_ptr->room) @@ -737,6 +738,7 @@ bool ladish_room_load_project(ladish_room_handle room_handle, const char * proje ladish_graph_trick_dicts(room_ptr->graph); ladish_app_supervisor_autorun(room_ptr->app_supervisor); + ladish_recent_project_use(room_ptr->project_dir); ladish_room_emit_project_properties_changed(room_ptr); ret = true; diff --git a/daemon/save.c b/daemon/save.c index a3f73c14..e79fe1c8 100644 --- a/daemon/save.c +++ b/daemon/save.c @@ -40,12 +40,12 @@ bool ladish_write_string(int fd, const char * string) ret = write(fd, string, len); if (ret == -1) { - log_error("write(%d, \"%s\", %zu) failed to write config file: %d (%s)", fd, string, len, errno, strerror(errno)); + log_error("write(%d, \"%s\", %zu) failed to write file: %d (%s)", fd, string, len, errno, strerror(errno)); return false; } if ((size_t)ret != len) { - log_error("write() wrote wrong byte count to config file (%zd != %zu).", ret, len); + log_error("write() wrote wrong byte count to file (%zd != %zu).", ret, len); return false; } diff --git a/dbus_constants.h b/dbus_constants.h index d24278dc..09be12e2 100644 --- a/dbus_constants.h +++ b/dbus_constants.h @@ -43,6 +43,7 @@ #define APPLICATION_OBJECT_PATH DBUS_BASE_PATH "/Application" #define IFACE_APPLICATION DBUS_NAME_BASE ".App" #define IFACE_GRAPH_DICT DBUS_NAME_BASE ".GraphDict" +#define IFACE_RECENT_ITEMS DBUS_NAME_BASE ".RecentItems" #define JMCORE_SERVICE_NAME DBUS_NAME_BASE ".jmcore" #define JMCORE_IFACE JMCORE_SERVICE_NAME diff --git a/wscript b/wscript index 95571bc9..a182d486 100644 --- a/wscript +++ b/wscript @@ -294,6 +294,8 @@ def build(bld): 'room.c', 'room_save.c', 'room_load.c', + 'recent_store.c', + 'recent_projects.c', ]: daemon.source.append(os.path.join("daemon", source)) -- 2.11.4.GIT