Compare commits

...

6 Commits

8 changed files with 1258 additions and 116 deletions

View File

@ -1,6 +1,6 @@
#include "config.h"
#include "wm.h"
#include <assert.h> #include <assert.h>
#include <fcntl.h>
#include <limits.h>
#include <stdint.h> #include <stdint.h>
#include <errno.h> #include <errno.h>
#include <stdbool.h> #include <stdbool.h>
@ -10,6 +10,10 @@
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "config.h"
#include "wm.h"
void wm_cfg_init_def(Config *config) void wm_cfg_init_def(Config *config)
{ {
@ -20,6 +24,20 @@ void wm_cfg_init_def(Config *config)
config->focus_on_motion = true; config->focus_on_motion = true;
wm_keybinds_init_def(config); wm_keybinds_init_def(config);
char log_file_dir[PATH_MAX] = {0};
const char* log_file_name = "wm.json";
char *xdg_data_home = getenv("XDG_DATA_HOME");
if (!xdg_data_home) {
char *home = getenv("HOME");
assert(home);
snprintf(log_file_dir, sizeof(log_file_dir), "%s/.local/share", home);
} else {
strncpy(log_file_dir, xdg_data_home, PATH_MAX);
}
snprintf(config->log_path, PATH_MAX, "%s/%s", log_file_dir, log_file_name);
} }
void wm_keybinds_init_def(Config *config) void wm_keybinds_init_def(Config *config)

View File

@ -78,6 +78,7 @@ typedef struct {
Keybind *keybinds; Keybind *keybinds;
int kb_count; int kb_count;
bool focus_on_motion; bool focus_on_motion;
char log_path[PATH_MAX];
} Config; } Config;
void wm_cfg_init_def(Config *config); void wm_cfg_init_def(Config *config);

View File

@ -105,7 +105,7 @@ void wm_maprequest_handler(Wm *wm, XMapRequestEvent e)
wm_client_focus(wm, c); wm_client_focus(wm, c);
if (wm->log_after_maprequest) { if (wm->log_after_maprequest) {
wm_log_state(wm, "after wm_kb_spawn", WM_LOGFILENAME); wm_log_state(wm, "after wm_kb_spawn", wm->config.log_path);
wm->log_after_maprequest = false; wm->log_after_maprequest = false;
} }
} }

View File

@ -1,11 +1,24 @@
#include "util.h" #include <stdint.h>
#include "client.h"
#include "wm.h"
#include <stdarg.h> #include <stdarg.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <setjmp.h> #include <setjmp.h>
#include <cmocka.h> #include <cmocka.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <limits.h>
#include <json-c/json_tokener.h>
#include "util.h"
#include "client.h"
#include "wm.h"
typedef struct {
char logfilepath[PATH_MAX];
char *jsonstr;
char *logjsonstr;
} TestGroup1State;
// store a copy of the nodes created in test2_setup, to check if any // store a copy of the nodes created in test2_setup, to check if any
// of the tests modified them // of the tests modified them
@ -323,6 +336,187 @@ static void test_wm_treenode_remove_client2(void **state)
wm_treenode_free(client_node3); wm_treenode_free(client_node3);
} }
static void test_wm_ptrarray_2d_index(void **state)
{
size_t x = 5;
size_t y = 10;
UIntArray *uintarr = wm_uintarray_new();
_Static_assert(sizeof(uint64_t) == sizeof(uint64_t*), "static assert failed");
for (size_t i = 0; i < x; i++) {
wm_uintarray_push(uintarr, (uint64_t)calloc(y, sizeof(uintptr_t)));
}
for (size_t i = 0; i < uintarr->size; i++) {
for (size_t j = 0; j < y; j++) {
((uint64_t**)uintarr->elements)[i][j] = i * j;
}
}
for (size_t i = 0; i < uintarr->size; i++) {
for (size_t j = 0; j < y; j++) {
assert_int_equal(wm_uintarray_2d_index(uintarr, i, j), i * j);
}
}
for (size_t i = 0; i < uintarr->size; i++) {
free((void*)uintarr->elements[i]);
}
wm_uintarray_free(uintarr);
}
static void test_wm_treenode_to_str(void **state)
{
TreeNode *node = wm_treenode_new(NODE_CLIENT, (void*)54321);
node->pos.x = 1;
node->pos.y = 2;
node->pos.w = 3;
node->pos.h = 4;
node->id = 5;
node->client = &(Client) {.name = "client_name", .is_pid_set = true, .pid = 12341234};
char *str = wm_treenode_to_str(node);
assert_non_null(strstr(str, "\"type\":\"NODE_CLIENT\","));
assert_non_null(strstr(str, "\"parent\":54321,"));
assert_non_null(strstr(str, "\"children\":[],"));
assert_non_null(strstr(str, "\"pos\":{"));
assert_non_null(strstr(str, "\"x\":1,"));
assert_non_null(strstr(str, "\"y\":2,"));
assert_non_null(strstr(str, "\"w\":3,"));
assert_non_null(strstr(str, "\"h\":4"));
assert_non_null(strstr(str, "\"client\":{"));
assert_non_null(strstr(str, "\"name\":\"client_name\""));
assert_non_null(strstr(str, "\"is_pid_set\":true"));
assert_non_null(strstr(str, "\"pid\":12341234"));
assert_non_null(strstr(str, "\"id\":5"));
wm_treenode_free(node);
free(str);
}
static void test_wm_json_obj_to_treenode(void **state)
{
TestGroup1State *_state = (TestGroup1State*)(*state);
struct json_object *parsed_json = json_tokener_parse(_state->jsonstr);
assert_non_null(parsed_json);
TreeNode *node = wm_json_obj_to_treenode(parsed_json);
assert_int_equal(node->type, NODE_VERTICAL);
assert_int_equal(node->parent, 0);
assert_int_equal(node->children->size, 2);
assert_int_equal(node->pos.x, 1);
assert_int_equal(node->pos.y, 2);
assert_int_equal(node->pos.w, 3);
assert_int_equal(node->pos.h, 4);
assert_null(node->client);
TreeNode *child1 = node->children->nodes[0];
assert_int_equal(child1->type, NODE_CLIENT);
// assert_int_equal(child1->parent, 0);
assert_int_equal(child1->children->size, 0);
assert_int_equal(child1->pos.x, 2);
assert_int_equal(child1->pos.y, 2);
assert_int_equal(child1->pos.w, 798);
assert_int_equal(child1->pos.h, 896);
assert_non_null(child1->client);
assert_string_equal(child1->client->name, "child1_client_name");
assert_true(child1->client->is_pid_set);
assert_int_equal(child1->client->pid, 19058);
assert_int_equal(child1->id, 10);
TreeNode *child2 = node->children->nodes[1];
assert_int_equal(child2->type, NODE_CLIENT);
// assert_int_equal(child2->parent, 0);
assert_int_equal(child2->children->size, 0);
assert_int_equal(child2->pos.x, 800);
assert_int_equal(child2->pos.y, 2);
assert_int_equal(child2->pos.w, 798);
assert_int_equal(child2->pos.h, 896);
assert_non_null(child2->client);
assert_string_equal(child2->client->name, "child2_client_name");
assert_true(child2->client->is_pid_set);
assert_int_equal(child2->client->pid, 19063);
assert_int_equal(child2->id, 11);
json_object_put(parsed_json);
free(node->children->nodes[0]->client->name);
free(node->children->nodes[0]->client);
wm_treenode_free(node->children->nodes[0]);
free(node->children->nodes[1]->client->name);
free(node->children->nodes[1]->client);
wm_treenode_free(node->children->nodes[1]);
wm_treenode_free(node);
}
static void test_wm_read_log(void **state)
{
TestGroup1State *_state = (TestGroup1State*)(*state);
UIntArray *entries = wm_read_log(_state->logfilepath);
assert_int_equal(entries->size, 1);
LogEntry *entry = (void*)wm_uintarray_at(entries, 0);
assert_int_equal(entry->timestamp, 1705601049);
assert_string_equal(entry->function_name, "wm_kb_spawn");
assert_true(entry->after);
// TODO check nodes
free(entry->workspaces->nodes[2]->children->nodes[0]->client->name);
free(entry->workspaces->nodes[2]->children->nodes[0]->client);
wm_treenode_free(entry->workspaces->nodes[2]->children->nodes[0]);
free(entry->workspaces->nodes[2]->children->nodes[1]->client->name);
free(entry->workspaces->nodes[2]->children->nodes[1]->client);
wm_treenode_free(entry->workspaces->nodes[2]->children->nodes[1]);
wm_treenode_free(entry->workspaces->nodes[2]);
wm_nodearray_free(entry->workspaces);
free(entry);
wm_uintarray_free(entries);
}
uint64_t simple_update_cost_function(TreeNode* node1, TreeNode* node2)
{
return 10;
}
static void test_wm_logentries_calculate_distances(void **state)
{
TestGroup1State *_state = (TestGroup1State*)(*state);
UIntArray *entries = wm_read_log(_state->logfilepath);
TreeNode *node = wm_treenode_new(NODE_VERTICAL, NULL);
TreeNode *child = wm_treenode_new(NODE_CLIENT, NULL);
wm_treenode_add_child(node, child);
wm_logentries_calculate_distances(entries, node, &simple_update_cost_function);
LogEntry *entry = (void*)wm_uintarray_at(entries, 0);
// TODO check node distances
free(entry->workspaces->nodes[2]->children->nodes[0]->client->name);
free(entry->workspaces->nodes[2]->children->nodes[0]->client);
free(entry->workspaces->nodes[2]->children->nodes[1]->client->name);
free(entry->workspaces->nodes[2]->children->nodes[1]->client);
wm_treenode_free(entry->workspaces->nodes[2]->children->nodes[0]);
wm_treenode_free(entry->workspaces->nodes[2]->children->nodes[1]);
wm_logentries_free(entries);
wm_treenode_free(node);
wm_treenode_free(child);
}
static void test_wm_postorder_traversal(void **state) static void test_wm_postorder_traversal(void **state)
{ {
TreeNode *node1 = (TreeNode*)*state; TreeNode *node1 = (TreeNode*)*state;
@ -392,14 +586,14 @@ static void test_wm_is_treenode_keyroot(void **state)
assert_non_null(postorder); assert_non_null(postorder);
assert_int_equal(postorder->size, 8); assert_int_equal(postorder->size, 8);
assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[0]), false); assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[0], postorder), false);
assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[1]), true); assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[1], postorder), true);
assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[2]), true); assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[2], postorder), true);
assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[3]), false); assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[3], postorder), false);
assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[4]), false); assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[4], postorder), false);
assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[5]), true); assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[5], postorder), true);
assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[6]), true); assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[6], postorder), true);
assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[7]), true); assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[7], postorder), true);
wm_nodearray_free(postorder); wm_nodearray_free(postorder);
} }
@ -425,10 +619,59 @@ static void test_wm_treenode_all_lmds(void **state)
wm_nodearray_free(lmds); wm_nodearray_free(lmds);
} }
static void test_wm_treenode_all_lmds_index(void **state)
{
TreeNode *node1 = (TreeNode*)*state;
NodeArray *postorder = wm_postorder_traversal(node1);
UIntArray *lmds = wm_treenode_all_lmds_index(node1);
assert_non_null(lmds);
assert_int_equal(lmds->size, 8);
assert_int_equal(wm_uintarray_at(lmds, 0), 0);
assert_int_equal(wm_uintarray_at(lmds, 1), 1);
assert_int_equal(wm_uintarray_at(lmds, 2), 2);
assert_int_equal(wm_uintarray_at(lmds, 3), 0);
assert_int_equal(wm_uintarray_at(lmds, 4), 4);
assert_int_equal(wm_uintarray_at(lmds, 5), 5);
assert_int_equal(wm_uintarray_at(lmds, 6), 4);
assert_int_equal(wm_uintarray_at(lmds, 7), 0);
assert_ptr_equal(postorder->nodes[wm_uintarray_at(lmds, 0)],
wm_treenode_lmd(postorder->nodes[0]));
assert_ptr_equal(postorder->nodes[wm_uintarray_at(lmds, 1)],
wm_treenode_lmd(postorder->nodes[1]));
assert_ptr_equal(postorder->nodes[wm_uintarray_at(lmds, 2)],
wm_treenode_lmd(postorder->nodes[2]));
assert_ptr_equal(postorder->nodes[wm_uintarray_at(lmds, 3)],
wm_treenode_lmd(postorder->nodes[3]));
assert_ptr_equal(postorder->nodes[wm_uintarray_at(lmds, 4)],
wm_treenode_lmd(postorder->nodes[4]));
assert_ptr_equal(postorder->nodes[wm_uintarray_at(lmds, 5)],
wm_treenode_lmd(postorder->nodes[5]));
assert_ptr_equal(postorder->nodes[wm_uintarray_at(lmds, 6)],
wm_treenode_lmd(postorder->nodes[6]));
assert_ptr_equal(postorder->nodes[wm_uintarray_at(lmds, 7)],
wm_treenode_lmd(postorder->nodes[7]));
wm_nodearray_free(postorder);
wm_uintarray_free(lmds);
}
static void test_wm_treenode_all_keyroots(void **state) static void test_wm_treenode_all_keyroots(void **state)
{ {
TreeNode *node1 = (TreeNode*)*state; TreeNode *node1 = (TreeNode*)*state;
NodeArray *postorder = wm_postorder_traversal(node1);
NodeArray *keyroots = wm_treenode_all_keyroots(node1); NodeArray *keyroots = wm_treenode_all_keyroots(node1);
assert_non_null(keyroots); assert_non_null(keyroots);
@ -440,9 +683,119 @@ static void test_wm_treenode_all_keyroots(void **state)
assert_int_equal(keyroots->nodes[3]->id, 3); assert_int_equal(keyroots->nodes[3]->id, 3);
assert_int_equal(keyroots->nodes[4]->id, 1); assert_int_equal(keyroots->nodes[4]->id, 1);
assert_true(wm_is_treenode_keyroot(keyroots->nodes[0], postorder));
assert_true(wm_is_treenode_keyroot(keyroots->nodes[1], postorder));
assert_true(wm_is_treenode_keyroot(keyroots->nodes[2], postorder));
assert_true(wm_is_treenode_keyroot(keyroots->nodes[3], postorder));
assert_true(wm_is_treenode_keyroot(keyroots->nodes[4], postorder));
wm_nodearray_free(postorder);
wm_nodearray_free(keyroots); wm_nodearray_free(keyroots);
} }
static void test_wm_treenode_all_keyroots_index(void **state)
{
TreeNode *node1 = (TreeNode*)*state;
NodeArray *postorder = wm_postorder_traversal(node1);
UIntArray *keyroot_indexes = wm_treenode_all_keyroots_index(node1);
assert_non_null(keyroot_indexes);
assert_int_equal(keyroot_indexes->size, 5);
assert_int_equal(keyroot_indexes->elements[0], 1);
assert_int_equal(keyroot_indexes->elements[1], 2);
assert_int_equal(keyroot_indexes->elements[2], 5);
assert_int_equal(keyroot_indexes->elements[3], 6);
assert_int_equal(keyroot_indexes->elements[4], 7);
assert_ptr_equal(postorder->nodes[wm_uintarray_at(keyroot_indexes, 0)],
postorder->nodes[1]);
assert_ptr_equal(postorder->nodes[wm_uintarray_at(keyroot_indexes, 1)],
postorder->nodes[2]);
assert_ptr_equal(postorder->nodes[wm_uintarray_at(keyroot_indexes, 2)],
postorder->nodes[5]);
assert_ptr_equal(postorder->nodes[wm_uintarray_at(keyroot_indexes, 3)],
postorder->nodes[6]);
assert_ptr_equal(postorder->nodes[wm_uintarray_at(keyroot_indexes, 4)],
postorder->nodes[7]);
assert_true(wm_is_treenode_keyroot(postorder->nodes[wm_uintarray_at(
keyroot_indexes, 0)], postorder));
assert_true(wm_is_treenode_keyroot(postorder->nodes[wm_uintarray_at(
keyroot_indexes, 1)], postorder));
assert_true(wm_is_treenode_keyroot(postorder->nodes[wm_uintarray_at(
keyroot_indexes, 2)], postorder));
assert_true(wm_is_treenode_keyroot(postorder->nodes[wm_uintarray_at(
keyroot_indexes, 3)], postorder));
assert_true(wm_is_treenode_keyroot(postorder->nodes[wm_uintarray_at(
keyroot_indexes, 4)], postorder));
wm_nodearray_free(postorder);
wm_uintarray_free(keyroot_indexes);
}
static void test_wm_tree_edit_distance(void **state)
{
TreeNode *a_root = wm_treenode_new(NODE_VERTICAL, NULL);
TreeNode *a_child1 = wm_treenode_new(NODE_VERTICAL, NULL);
TreeNode *a_child2 = wm_treenode_new(NODE_VERTICAL, NULL);
TreeNode *a_child3 = wm_treenode_new(NODE_VERTICAL, NULL);
TreeNode *a_child4 = wm_treenode_new(NODE_VERTICAL, NULL);
TreeNode *a_child5 = wm_treenode_new(NODE_VERTICAL, NULL);
TreeNode *b_root = wm_treenode_new(NODE_VERTICAL, NULL);
TreeNode *b_child1 = wm_treenode_new(NODE_VERTICAL, NULL);
TreeNode *b_child2 = wm_treenode_new(NODE_VERTICAL, NULL);
TreeNode *b_child3 = wm_treenode_new(NODE_VERTICAL, NULL);
TreeNode *b_child4 = wm_treenode_new(NODE_VERTICAL, NULL);
TreeNode *b_child5 = wm_treenode_new(NODE_VERTICAL, NULL);
wm_treenode_add_child(a_root, a_child1);
wm_treenode_add_child(a_root, a_child2);
wm_treenode_add_child(a_child1, a_child3);
wm_treenode_add_child(a_child1, a_child4);
wm_treenode_add_child(a_child4, a_child5);
wm_treenode_add_child(b_root, b_child1);
wm_treenode_add_child(b_root, b_child2);
wm_treenode_add_child(b_child1, b_child3);
wm_treenode_add_child(b_child3, b_child4);
wm_treenode_add_child(b_child3, b_child5);
size_t dist = wm_tree_edit_distance(a_root, b_root, (TreeEditDistanceCosts) {
.insert_cost = 10,
.remove_cost = 10,
.update_cost_function = &simple_update_cost_function,
});
assert_int_equal(dist, 70);
wm_treenode_free(a_root);
wm_treenode_free(a_child1);
wm_treenode_free(a_child2);
wm_treenode_free(a_child3);
wm_treenode_free(a_child4);
wm_treenode_free(a_child5);
wm_treenode_free(b_root);
wm_treenode_free(b_child1);
wm_treenode_free(b_child2);
wm_treenode_free(b_child3);
wm_treenode_free(b_child4);
wm_treenode_free(b_child5);
}
static int test2_setup(void **state) static int test2_setup(void **state)
{ {
TreeNode *node1 = wm_treenode_new(NODE_VERTICAL, NULL); TreeNode *node1 = wm_treenode_new(NODE_VERTICAL, NULL);
@ -463,13 +816,13 @@ static int test2_setup(void **state)
assert_non_null(node7); assert_non_null(node7);
assert_non_null(node8); assert_non_null(node8);
wm_nodearray_push(node1->children, node2); wm_treenode_add_child(node1, node2);
wm_nodearray_push(node1->children, node3); wm_treenode_add_child(node1, node3);
wm_nodearray_push(node2->children, node4); wm_treenode_add_child(node2, node4);
wm_nodearray_push(node2->children, node5); wm_treenode_add_child(node2, node5);
wm_nodearray_push(node2->children, node6); wm_treenode_add_child(node2, node6);
wm_nodearray_push(node3->children, node7); wm_treenode_add_child(node3, node7);
wm_nodearray_push(node3->children, node8); wm_treenode_add_child(node3, node8);
node1->id = 1; node1->id = 1;
node2->id = 2; node2->id = 2;
@ -543,6 +896,117 @@ static int test2_teardown(void **state)
return 0; return 0;
} }
static int test1_setup(void **state)
{
*state = calloc(1, sizeof(TestGroup1State));
TestGroup1State *_state = (TestGroup1State*)(*state);
_state->jsonstr = "{"
"\"type\":\"NODE_VERTICAL\","
"\"parent\":0,"
"\"children\":["
"{"
"\"type\":\"NODE_CLIENT\","
"\"parent\":105965433127232,"
"\"children\":[],"
"\"pos\":{"
"\"x\":2,"
"\"y\":2,"
"\"w\":798,"
"\"h\":896"
"},"
"\"client\":{"
"\"address\":106102872080672,"
"\"name\":\"child1_client_name\","
"\"is_pid_set\":true,"
"\"pid\":19058"
"},"
"\"id\":10"
"},"
"{"
"\"type\":\"NODE_CLIENT\","
"\"parent\":105965433127232,"
"\"children\":[],"
"\"pos\":{"
"\"x\":800,"
"\"y\":2,"
"\"w\":798,"
"\"h\":896"
"},"
"\"client\":{"
"\"address\":106102872080800,"
"\"name\":\"child2_client_name\","
"\"is_pid_set\":true,"
"\"pid\":19063"
"},"
"\"id\":11"
"}"
"],"
"\"pos\":{"
"\"x\":1,"
"\"y\":2,"
"\"w\":3,"
"\"h\":4"
"},"
"\"client\":{"
"\"address\":1234,"
"\"name\":\"client_name\","
"\"is_pid_set\":true,"
"\"pid\":1234"
"},"
"\"id\":5"
"}";
size_t logjsonstr_size = strlen(_state->jsonstr) + 1024;
_state->logjsonstr = calloc(logjsonstr_size, 1);
snprintf(_state->logjsonstr, logjsonstr_size, "{\"1705601049:wm_kb_spawn:after\": {"
"\"2\":"
"%s"
"}}", _state->jsonstr);
const char *basedir = "./";
assert_true(access(basedir, W_OK) >= 0);
srand(time(NULL));
bool ok = false;
int tries = 0;
while (!ok && tries < 1000) {
snprintf(_state->logfilepath, PATH_MAX, "%s%ld.json", basedir, random());
if (access(_state->logfilepath, F_OK) < 0)
ok = true;
tries++;
}
if (!ok)
return 1;
int fd = creat(_state->logfilepath, 0664);
if (fd < 0) perror("creat");
assert_true(fd >= 0);
assert_true(write(fd, _state->logjsonstr, strlen(_state->logjsonstr)) > 0);
assert_true(close(fd) >= 0);
return 0;
}
static int test1_teardown(void **state)
{
TestGroup1State *_state = (TestGroup1State*)(*state);
int ret = unlink(_state->logfilepath);
if (ret < 0) {
perror("unlink");
return 1;
}
free(_state->logjsonstr);
free(*state);
return 0;
}
int main(void) int main(void)
{ {
const struct CMUnitTest test_group1[] = { const struct CMUnitTest test_group1[] = {
@ -553,6 +1017,11 @@ int main(void)
cmocka_unit_test(test_wm_find_client_nodes), cmocka_unit_test(test_wm_find_client_nodes),
cmocka_unit_test(test_wm_treenode_remove_client1), cmocka_unit_test(test_wm_treenode_remove_client1),
cmocka_unit_test(test_wm_treenode_remove_client2), cmocka_unit_test(test_wm_treenode_remove_client2),
cmocka_unit_test(test_wm_ptrarray_2d_index),
cmocka_unit_test(test_wm_treenode_to_str),
cmocka_unit_test(test_wm_json_obj_to_treenode),
cmocka_unit_test(test_wm_read_log),
cmocka_unit_test(test_wm_logentries_calculate_distances),
}; };
const struct CMUnitTest test_group2[] = { const struct CMUnitTest test_group2[] = {
@ -560,9 +1029,12 @@ int main(void)
cmocka_unit_test(test_wm_treenode_lmd), cmocka_unit_test(test_wm_treenode_lmd),
cmocka_unit_test(test_wm_is_treenode_keyroot), cmocka_unit_test(test_wm_is_treenode_keyroot),
cmocka_unit_test(test_wm_treenode_all_lmds), cmocka_unit_test(test_wm_treenode_all_lmds),
cmocka_unit_test(test_wm_treenode_all_lmds_index),
cmocka_unit_test(test_wm_treenode_all_keyroots), cmocka_unit_test(test_wm_treenode_all_keyroots),
cmocka_unit_test(test_wm_treenode_all_keyroots_index),
cmocka_unit_test(test_wm_tree_edit_distance),
}; };
cmocka_run_group_tests(test_group1, NULL, NULL); cmocka_run_group_tests(test_group1, test1_setup, test1_teardown);
cmocka_run_group_tests(test_group2, test2_setup, test2_teardown); cmocka_run_group_tests(test_group2, test2_setup, test2_teardown);
} }

View File

@ -1,13 +1,23 @@
#include "util.h" #include <fcntl.h>
#include "wm.h" #include <stdint.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h> #include <time.h>
#include <json-c/json_object.h>
#include <json-c/json_tokener.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#include <unistd.h>
#include <libgen.h>
#include <json-c/arraylist.h>
#include <json-c/json_object_iterator.h>
#include <json-c/json_util.h>
#include <json-c/json_object.h>
#include <json-c/json_tokener.h>
#include "util.h"
#include "client.h"
#include "wm.h"
static unsigned int node_id = 0; static unsigned int node_id = 0;
@ -104,6 +114,15 @@ void wm_treenode_swap(TreeNode *root, TreeNode *node1, TreeNode* node2)
node2->client = tmp; node2->client = tmp;
} }
void wm_treenode_add_child(TreeNode *node, TreeNode *child)
{
assert(node);
assert(child);
wm_nodearray_push(node->children, child);
child->parent = node;
}
void wm_node_type_to_str(NodeType type, char *buf, size_t bufsize) void wm_node_type_to_str(NodeType type, char *buf, size_t bufsize)
{ {
switch (type) { switch (type) {
@ -124,6 +143,23 @@ void wm_node_type_to_str(NodeType type, char *buf, size_t bufsize)
} }
} }
NodeType wm_node_type_from_str(char *buf, size_t bufsize)
{
char *types[4] = {0};
types[NODE_CLIENT] = "NODE_CLIENT";
types[NODE_VERTICAL] = "NODE_VERTICAL";
types[NODE_HORIZONTAL] = "NODE_HORIZONTAL";
types[NODE_TAB] = "NODE_TAB";
for (size_t i = 0; i < 4; i++) {
if (strncmp(buf, types[i], bufsize) == 0)
return (NodeType)i;
}
fprintf(stderr, "wm: invalid node type string: %s. exiting\n", buf);
exit(1);
}
char* wm_treenode_to_str(TreeNode *node) char* wm_treenode_to_str(TreeNode *node)
{ {
assert(node); assert(node);
@ -166,9 +202,10 @@ char* wm_treenode_to_str(TreeNode *node)
return ret; return ret;
} }
PtrArray wm_nonempty_workspaces_to_strptrarray(Wm *wm) UIntArray* wm_nonempty_workspaces_to_strptrarray(Wm *wm)
{ {
PtrArray ret = wm_ptrarray_new(); _Static_assert(sizeof(uint64_t) == sizeof(uint64_t*), "static assert failed");
UIntArray *ret = wm_uintarray_new();
for (size_t i = 0; i < wm->smon->wscount; i++) { for (size_t i = 0; i < wm->smon->wscount; i++) {
TreeNode *node = wm->smon->workspaces[i].tree; TreeNode *node = wm->smon->workspaces[i].tree;
@ -179,11 +216,11 @@ PtrArray wm_nonempty_workspaces_to_strptrarray(Wm *wm)
.ws_index = i, .ws_index = i,
.str = wm_treenode_to_str(wm->smon->workspaces[i].tree) .str = wm_treenode_to_str(wm->smon->workspaces[i].tree)
}; };
wm_ptrarray_push(&ret, ws_str_with_index); wm_uintarray_push(ret, (uint64_t)ws_str_with_index);
} }
} }
DEBUG_PRINT("%s returning %ld\n", __func__, ret.size); DEBUG_PRINT("%s returning %ld\n", __func__, ret->size);
return ret; return ret;
} }
@ -193,34 +230,83 @@ void wm_log_state(Wm *wm, const char *prefixstr, const char* logfile)
RETURN_IF_NULL(prefixstr); RETURN_IF_NULL(prefixstr);
RETURN_IF_NULL(logfile); RETURN_IF_NULL(logfile);
FILE *fptr = fopen(logfile, "a"); UIntArray *ws_str = wm_nonempty_workspaces_to_strptrarray(wm);
if (!fptr) {
fprintf(stderr, "wm: could not open log file %s\n", logfile);
return;
}
PtrArray ws_str = wm_nonempty_workspaces_to_strptrarray(wm);
char str[128] = {0}; char str[128] = {0};
char prefix[WM_PREFIX_LEN + 32] = {0}; char prefix[WM_PREFIX_LEN + 32] = {0};
snprintf(prefix, sizeof(prefix), "%lu_%s", time(NULL), prefixstr); snprintf(prefix, sizeof(prefix), "%lu:%s", time(NULL), prefixstr);
json_object *log_entry_obj = json_object_new_object(); json_object *log_entry_obj = json_object_new_object();
json_object_object_add(log_entry_obj, prefix, json_object_new_object());
for (size_t i = 0; i < ws_str.size; i++) { for (size_t i = 0; i < ws_str->size; i++) {
WmWorkspaceToStrRet *ws_with_index = (WmWorkspaceToStrRet*)ws_str.ptrs[i]; WmWorkspaceToStrRet *ws_with_index = (WmWorkspaceToStrRet*)
wm_uintarray_at(ws_str, i);
snprintf(str, sizeof(str), "%ld", ws_with_index->ws_index); snprintf(str, sizeof(str), "%ld", ws_with_index->ws_index);
json_object_object_add(json_object_object_get(log_entry_obj, prefix), str, json_tokener_parse(ws_with_index->str)); json_object_object_add(log_entry_obj, str, json_tokener_parse(ws_with_index->str));
fprintf(fptr, "%s,\n", json_object_to_json_string_ext(log_entry_obj, JSON_C_TO_STRING_PRETTY));
free(ws_with_index->str); free(ws_with_index->str);
free(ws_with_index); free(ws_with_index);
} }
json_object_put(log_entry_obj); int fd = -1;
fflush(fptr); struct json_object *jobj = NULL;
fclose(fptr); if (access(logfile, F_OK) != 0) {
wm_ptrarray_free(&ws_str); fd = creat(logfile, 0644);
if (fd < 0) {
perror("creat");
goto ret;
}
jobj = json_object_new_object();
} else {
fd = open(logfile, O_RDWR);
if (fd < 0) {
perror("open");
goto ret;
}
jobj = json_object_from_fd(fd);
if (!jobj) {
const char *err = json_util_get_last_err();
fprintf(stderr, "wm: could not read json: %s\n", err);
// TODO maybe free
goto ret;
}
}
json_object_object_add(jobj, prefix, log_entry_obj);
int ret = close(fd);
if (ret < 0) {
perror("close");
goto ret;
}
ret = open(logfile, O_TRUNC | O_WRONLY);
if (ret < 0) {
perror("open");
goto ret;
}
json_object_to_fd(fd, jobj, JSON_C_TO_STRING_PRETTY);
ret = close(fd);
if (ret < 0) {
perror("close");
goto ret;
}
// TODO
wm_logentries_free(wm->log_entries);
wm->log_entries = wm_read_log(logfile);
ret:
json_object_put(jobj);
wm_uintarray_free(ws_str);
} }
TreeNode* wm_treenode_lmd(TreeNode *node) TreeNode* wm_treenode_lmd(TreeNode *node)
@ -249,6 +335,30 @@ NodeArray* wm_treenode_all_lmds(TreeNode *node)
return ret; return ret;
} }
UIntArray* wm_treenode_all_lmds_index(TreeNode *node)
{
assert(node);
NodeArray *postorder = wm_postorder_traversal(node);
UIntArray *ret = wm_uintarray_new();
for (size_t i = 0; i < postorder->size; i++) {
TreeNode *lmd = wm_treenode_lmd(postorder->nodes[i]);
uintptr_t index = UINT_MAX;
for (index = 0; index < postorder->size; index++) {
if (postorder->nodes[index]->id == lmd->id)
break;
}
assert(index != UINT_MAX);
wm_uintarray_push(ret, index);
}
wm_nodearray_free(postorder);
return ret;
}
NodeArray* wm_postorder_traversal(TreeNode *tree) NodeArray* wm_postorder_traversal(TreeNode *tree)
{ {
assert(tree); assert(tree);
@ -308,12 +418,30 @@ NodeArray* wm_postorder_traversal(TreeNode *tree)
return ret; return ret;
} }
bool wm_is_treenode_keyroot(TreeNode *node) bool wm_is_treenode_keyroot(TreeNode *node, NodeArray *postorder)
{ {
assert(node); assert(node);
assert(postorder);
if (!node->parent) return true; if (!node->parent) return true;
return wm_treenode_lmd(node) != wm_treenode_lmd(node->parent); TreeNode *node_lmd = wm_treenode_lmd(node);
size_t node_index = SIZE_MAX;
for (size_t i = 0; i < postorder->size; i++) {
if (node->id == postorder->nodes[i]->id) {
node_index = i;
break;
}
}
assert(node_index != SIZE_MAX);
for (size_t i = node_index + 1; i < postorder->size; i++) {
if (node_lmd->id == wm_treenode_lmd(postorder->nodes[i])->id)
return false;
}
return true;
} }
NodeArray* wm_treenode_all_keyroots(TreeNode *node) NodeArray* wm_treenode_all_keyroots(TreeNode *node)
@ -325,7 +453,7 @@ NodeArray* wm_treenode_all_keyroots(TreeNode *node)
for (size_t i = 0; i < postorder->size; i++) { for (size_t i = 0; i < postorder->size; i++) {
TreeNode *node = postorder->nodes[i]; TreeNode *node = postorder->nodes[i];
if (wm_is_treenode_keyroot(node)) if (wm_is_treenode_keyroot(node, postorder))
wm_nodearray_push(ret, node); wm_nodearray_push(ret, node);
} }
@ -333,6 +461,192 @@ NodeArray* wm_treenode_all_keyroots(TreeNode *node)
return ret; return ret;
} }
UIntArray* wm_treenode_all_keyroots_index(TreeNode *node)
{
assert(node);
NodeArray *postorder = wm_postorder_traversal(node);
UIntArray *ret = wm_uintarray_new();
for (size_t i = 0; i < postorder->size; i++) {
if (wm_is_treenode_keyroot(postorder->nodes[i], postorder))
wm_uintarray_push(ret, i);
}
wm_nodearray_free(postorder);
return ret;
}
bool wm_nodes_are_equal(TreeNode *node1, TreeNode *node2)
{
if (node1->type != node2->type)
return false;
return true;
}
// https://github.com/timtadh/zhang-shasha
void wm_treedist(TreeNode *tree1, TreeNode *tree2, size_t i, size_t j,
UIntArray *dists, TreeEditDistanceCosts costs)
{
assert(tree1);
assert(tree2);
NodeArray *tree1_postorder = wm_postorder_traversal(tree1);
NodeArray *tree2_postorder = wm_postorder_traversal(tree2);
UIntArray *tree1_lmds = wm_treenode_all_lmds_index(tree1);
UIntArray *tree2_lmds = wm_treenode_all_lmds_index(tree2);
size_t m = i - wm_uintarray_at(tree1_lmds, i) + 2;
size_t n = j - wm_uintarray_at(tree2_lmds, j) + 2;
UIntArray *fd = wm_uintarray_new();
_Static_assert(sizeof(uint64_t) == sizeof(uint64_t*), "static assert failed");
for (size_t k = 0; k < m; k++) {
wm_uintarray_push(fd, (uint64_t)calloc(n, sizeof(uint64_t)));
}
size_t ioff = wm_uintarray_at(tree1_lmds, i) - 1;
size_t joff = wm_uintarray_at(tree2_lmds, j) - 1;
for (size_t x = 1; x < m; x++) {
TreeNode *node = tree1_postorder->nodes[x + ioff];
((uint64_t**)fd->elements)[x][0] = ((size_t)wm_uintarray_2d_index(fd,
x - 1, 0) + (size_t)costs.remove_cost);
}
for (size_t y = 1; y < n; y++) {
TreeNode *node = tree2_postorder->nodes[y + joff];
((uint64_t**)fd->elements)[0][y] = ((size_t)wm_uintarray_2d_index(fd,
0, y - 1) + (size_t)costs.insert_cost);
}
for (size_t x = 1; x < m; x++) {
for (size_t y = 1; y < n; y++) {
TreeNode *node1 = tree1_postorder->nodes[x + ioff];
TreeNode *node2 = tree2_postorder->nodes[y + joff];
if (wm_uintarray_at(tree1_lmds, i) == tree1_lmds->elements[x + ioff] &&
wm_uintarray_at(tree2_lmds, j) == tree2_lmds->elements[y + joff]) {
size_t _costs[3] = {
wm_uintarray_2d_index(fd, x - 1, y) + costs.remove_cost, //(node1)
wm_uintarray_2d_index(fd, x, y - 1) + costs.insert_cost, //(node2)
wm_uintarray_2d_index(fd, x - 1, y - 1) + (*costs.update_cost_function)(node1, node2),
};
size_t min = SIZE_MAX;
for (size_t k = 0; k < 3; k++) {
if (_costs[k] < min) {
min = _costs[k];
}
}
assert(min != SIZE_MAX);
((uint64_t**)fd->elements)[x][y] = min;
((uint64_t**)dists->elements)[x + ioff][y + joff] = min;
} else {
size_t p = tree1_lmds->elements[x + ioff] - 1 - ioff;
size_t q = tree2_lmds->elements[y + joff] - 1 - joff;
size_t _costs[3] = {
(size_t)wm_uintarray_2d_index(fd, x - 1, y) + costs.remove_cost, // (node1)
(size_t)wm_uintarray_2d_index(fd, x, y - 1) + costs.insert_cost, // (node2)
(size_t)wm_uintarray_2d_index(fd, p, q) +
(size_t)wm_uintarray_2d_index(dists, x + ioff, y + joff),
};
size_t min = SIZE_MAX;
for (size_t k = 0; k < 3; k++) {
if (_costs[k] < min) {
min = _costs[k];
}
}
assert(min != SIZE_MAX);
((uint64_t**)fd->elements)[x][y] = min;
}
}
}
for (size_t k = 0; k < m; k++) {
free((void*)wm_uintarray_at(fd, k));
}
wm_nodearray_free(tree1_postorder);
wm_nodearray_free(tree2_postorder);
wm_uintarray_free(tree1_lmds);
wm_uintarray_free(tree2_lmds);
wm_uintarray_free(fd);
}
// https://github.com/timtadh/zhang-shasha
size_t wm_tree_edit_distance(TreeNode *tree1, TreeNode *tree2, TreeEditDistanceCosts costs)
{
assert(tree1);
assert(tree2);
NodeArray *tree1_postorder = wm_postorder_traversal(tree1);
NodeArray *tree2_postorder = wm_postorder_traversal(tree2);
UIntArray *tree1_keyroot_indexes = wm_treenode_all_keyroots_index(tree1);
UIntArray *tree2_keyroot_indexes = wm_treenode_all_keyroots_index(tree2);
UIntArray *dists = wm_uintarray_new();
_Static_assert(sizeof(uint64_t) == sizeof(uint64_t*), "static assert failed");
for (size_t k = 0; k < tree1_postorder->size; k++) {
wm_uintarray_push(dists, (uint64_t)calloc(tree2_postorder->size, sizeof(uint64_t)));
}
for (size_t p = 0; p < tree1_keyroot_indexes->size; p++) {
for (size_t q = 0; q < tree2_keyroot_indexes->size; q++) {
size_t i = wm_uintarray_at(tree1_keyroot_indexes, p);
size_t j = wm_uintarray_at(tree2_keyroot_indexes, q);
wm_treedist(tree1, tree2, i, j, dists, costs);
}
}
size_t ret = wm_uintarray_2d_index(dists, tree1_postorder->size - 1, tree2_postorder->size - 1);
for (size_t k = 0; k < tree1_postorder->size; k++) {
free((void*)wm_uintarray_at(dists, k));
}
wm_nodearray_free(tree1_postorder);
wm_nodearray_free(tree2_postorder);
wm_uintarray_free(tree1_keyroot_indexes);
wm_uintarray_free(tree2_keyroot_indexes);
wm_uintarray_free(dists);
return ret;
}
void wm_logentries_calculate_distances(UIntArray *log_entries, TreeNode *curr_tree,
UpdateCostFunction update_cost_function)
{
TreeEditDistanceCosts costs = (TreeEditDistanceCosts) {
.insert_cost = 3,
.remove_cost = 100,
.update_cost_function = update_cost_function,
};
for (size_t i = 0; i < log_entries->size; i++) {
LogEntry *entry = (LogEntry*)wm_uintarray_at(log_entries, i);
for (size_t j = 0; j < entry->workspaces->size; j++) {
TreeNode *node = entry->workspaces->nodes[j];
if (node == NULL) continue;
node->distance = wm_tree_edit_distance(curr_tree, node, costs);
}
}
}
void wm_treenode_print(TreeNode *node) void wm_treenode_print(TreeNode *node)
{ {
char *str = wm_treenode_to_str(node); char *str = wm_treenode_to_str(node);
@ -340,6 +654,259 @@ void wm_treenode_print(TreeNode *node)
free(str); free(str);
} }
TreeNode* wm_json_obj_to_treenode(struct json_object *jobj)
{
assert(jobj);
TreeNode *tree_ret = wm_treenode_new(NODE_VERTICAL, NULL);
bool found_key = false;
struct json_object *ret;
struct json_object *tmp;
char *key = "type";
// TODO
found_key = json_object_object_get_ex(jobj, key, &ret);
if (!found_key) goto not_found_key;
const char *type = json_object_get_string(ret);
tree_ret->type = wm_node_type_from_str((char*)type, strlen(type));
key = "parent";
found_key = json_object_object_get_ex(jobj, key, &ret);
if (!found_key) goto not_found_key;
tree_ret->parent = (void*)json_object_get_int64(ret);
key = "id";
found_key = json_object_object_get_ex(jobj, key, &ret);
if (!found_key) goto not_found_key;
tree_ret->id = json_object_get_int64(ret);
if (tree_ret->type == NODE_CLIENT) {
tree_ret->client = calloc(1, sizeof(Client));
key = "client";
found_key = json_object_object_get_ex(jobj, key, &tmp);
if (!found_key) goto not_found_key;
key = "name";
found_key = json_object_object_get_ex(tmp, key, &ret);
if (!found_key) goto not_found_key;
tree_ret->client->name = strdup(json_object_get_string(ret));
key = "is_pid_set";
found_key = json_object_object_get_ex(tmp, key, &ret);
if (!found_key) goto not_found_key;
tree_ret->client->is_pid_set = json_object_get_boolean(ret);
key = "pid";
found_key = json_object_object_get_ex(tmp, key, &ret);
if (!found_key) goto not_found_key;
tree_ret->client->pid = json_object_get_int(ret);
}
// pos
key = "pos";
found_key = json_object_object_get_ex(jobj, key, &tmp);
if (!found_key) goto not_found_key;
key = "x";
found_key = json_object_object_get_ex(tmp, key, &ret);
if (!found_key) goto not_found_key;
tree_ret->pos.x = json_object_get_int(ret);
key = "y";
found_key = json_object_object_get_ex(tmp, key, &ret);
if (!found_key) goto not_found_key;
tree_ret->pos.y = json_object_get_int(ret);
key = "w";
found_key = json_object_object_get_ex(tmp, key, &ret);
if (!found_key) goto not_found_key;
tree_ret->pos.w = json_object_get_int(ret);
key = "h";
found_key = json_object_object_get_ex(tmp, key, &ret);
if (!found_key) goto not_found_key;
tree_ret->pos.h = json_object_get_int(ret);
// children
key = "children";
found_key = json_object_object_get_ex(jobj, key, &ret);
if (!found_key) goto not_found_key;
struct array_list *list = json_object_get_array(ret);
for (size_t i = 0; i < array_list_length(list); i++) {
struct json_object *child = array_list_get_idx(list, i);
wm_nodearray_push(tree_ret->children, wm_json_obj_to_treenode(child));
}
return tree_ret;
not_found_key:
fprintf(stderr, "wm: %s: could not find key %s\n", __func__, key);
return tree_ret;
}
#define CREATE_LOGFILE_ERROR(str) \
do { \
fprintf(stderr, "wm: could not create log file. error: %s. exiting.\n", str); \
exit(1); \
} while (0); \
static void recursive_mkdir(char *path, mode_t mode)
{
char *sep = strrchr(path, '/');
if(sep != NULL) {
*sep = '\0';
recursive_mkdir(path, mode);
*sep = '/';
}
if(mkdir(path, mode) < 0 && errno != EEXIST) {
fprintf(stderr, "wm: could not create directory. error %s. exiting.\n",
strerror(errno));
exit(1);
}
}
void wm_logfile_init(const char *path)
{
char *log_file_dir = dirname((char*)path);
if (access(path, F_OK) == 0) return;
int ret = access(log_file_dir, W_OK | R_OK);
if (ret == 0) {
ret = creat(path, 0664);
if (ret < 0) CREATE_LOGFILE_ERROR(strerror(errno));
ret = close(ret);
if (ret < 0) CREATE_LOGFILE_ERROR(strerror(errno));
return;
}
if (ret < 0 && errno == ENOENT) {
recursive_mkdir(log_file_dir, 0751);
ret = creat(path, 0664);
if (ret < 0) CREATE_LOGFILE_ERROR(strerror(errno));
ret = close(ret);
if (ret < 0) CREATE_LOGFILE_ERROR(strerror(errno));
} else {
CREATE_LOGFILE_ERROR(strerror(errno));
}
}
/* UIntArray<LogEntry*> */
UIntArray* wm_read_log(const char* filename)
{
_Static_assert(sizeof(LogEntry*) == sizeof(uint64_t), "");
// TODO maybe prealloc
UIntArray* ret = wm_uintarray_new();
int fd = -1;
struct json_object *jobj = NULL;
fd = open(filename, O_RDONLY);
if (fd < 0) {
perror("open");
return ret;
}
jobj = json_object_from_fd(fd);
if (!jobj) {
const char *err = json_util_get_last_err();
fprintf(stderr, "wm: could not read json: %s\n", err);
return ret;
}
char *token;
const char *name = NULL;
struct json_object *value = NULL;
struct json_object_iterator iter = json_object_iter_begin(jobj);
struct json_object_iterator iter_end = json_object_iter_end(jobj);
const char *ws_name = NULL;
struct json_object *ws_value = NULL;
struct json_object_iterator ws_iter = json_object_iter_init_default();
struct json_object_iterator ws_iter_end = json_object_iter_init_default();
while (!json_object_iter_equal(&iter, &iter_end)) {
// TODO maybe prealloc
LogEntry *entry = calloc(1, sizeof(LogEntry));
entry->workspaces = wm_nodearray_new();
name = json_object_iter_peek_name(&iter);
value = json_object_iter_peek_value(&iter);
int i = 0;
char *token = strtok((char*)name, ":");
while(token != NULL) {
i++;
switch (i) {
case 1:
// TODO
entry->timestamp = atol(token);
break;
case 2:
strncpy(entry->function_name, token, 128);
break;
case 3:
if (strncmp(token, "after", strlen("after")) == 0)
entry->after = true;
else {
fprintf(stderr, "wm: encountered invalid token %s\n. exiting.",
token);
abort();
}
break;
default:
fprintf(stderr, "wm: encountered invalid token %s\n. exiting.",
token);
abort();
}
token = strtok(NULL, ":");
}
ws_iter = json_object_iter_begin(value);
ws_iter_end = json_object_iter_end(value);
while (!json_object_iter_equal(&ws_iter, &ws_iter_end)) {
ws_name = json_object_iter_peek_name(&ws_iter);
ws_value = json_object_iter_peek_value(&ws_iter);
int ws_index = atoi(ws_name);
while (entry->workspaces->size < ws_index) {
wm_nodearray_push(entry->workspaces, NULL);
}
wm_nodearray_push(entry->workspaces, wm_json_obj_to_treenode(ws_value));
json_object_iter_next(&ws_iter);
}
wm_uintarray_push(ret, (uint64_t)entry);
json_object_iter_next(&iter);
}
json_object_put(jobj);
int result = close(fd);
if (result < 0)
perror("close");
return ret;
}
void wm_tree_to_DOT(TreeNode *root, const char *filename) void wm_tree_to_DOT(TreeNode *root, const char *filename)
{ {
DEBUG_PRINT("%s\n", __func__); DEBUG_PRINT("%s\n", __func__);
@ -671,45 +1238,54 @@ void wm_nodearray_free(NodeArray *arr)
free(arr); free(arr);
} }
PtrArray wm_ptrarray_new() UIntArray* wm_uintarray_new()
{ {
PtrArray arr; UIntArray *arr = calloc(1, sizeof(UIntArray));
assert(arr);
arr.capacity = 10; arr->capacity = 10;
arr.ptrs = calloc(arr.capacity, sizeof(void*)); arr->elements = calloc(arr->capacity, sizeof(void*));
assert(arr.ptrs); assert(arr->elements);
arr.size = 0; arr->size = 0;
return arr; return arr;
} }
void wm_ptrarray_push(PtrArray *arr, void* ptr) uint64_t wm_uintarray_at(UIntArray *arr, size_t index)
{
assert(arr);
assert(index < arr->size);
return arr->elements[index];
}
void wm_uintarray_push(UIntArray *arr, uint64_t element)
{ {
assert(arr); assert(arr);
arr->size++; arr->size++;
if (arr->size >= arr->capacity) { if (arr->size >= arr->capacity) {
void* temp = calloc(arr->capacity, sizeof(void*)); void* temp = calloc(arr->capacity, sizeof(uint64_t));
assert(temp); assert(temp);
memcpy(temp, arr->ptrs, arr->capacity * sizeof(void*)); memcpy(temp, arr->elements, arr->capacity * sizeof(uint64_t));
free(arr->ptrs); free(arr->elements);
size_t old_capacity = arr->capacity; size_t old_capacity = arr->capacity;
arr->capacity = old_capacity * 2; arr->capacity = old_capacity * 2;
arr->ptrs = calloc(arr->capacity, sizeof(void*)); arr->elements = calloc(arr->capacity, sizeof(uint64_t));
assert(arr->ptrs); assert(arr->elements);
memcpy(arr->ptrs, temp, old_capacity * sizeof(void*)); memcpy(arr->elements, temp, old_capacity * sizeof(uint64_t));
free(temp); free(temp);
arr->ptrs[arr->size - 1] = ptr; arr->elements[arr->size - 1] = element;
return; return;
} }
arr->ptrs[arr->size - 1] = ptr; arr->elements[arr->size - 1] = element;
} }
bool wm_ptrarray_pop(PtrArray *arr, void **ret) bool wm_uintarray_pop(UIntArray *arr, uint64_t *ret)
{ {
DEBUG_PRINT("%s\n", __func__); DEBUG_PRINT("%s\n", __func__);
assert(arr); assert(arr);
@ -718,12 +1294,12 @@ bool wm_ptrarray_pop(PtrArray *arr, void **ret)
if (arr->size == 0) if (arr->size == 0)
return false; return false;
*ret = arr->ptrs[arr->size - 1]; *ret = arr->elements[arr->size - 1];
arr->size--; arr->size--;
return true; return true;
} }
bool wm_ptrarray_pop_front(PtrArray *arr, void **ret) bool wm_uintarray_pop_front(UIntArray *arr, uint64_t *ret)
{ {
assert(arr); assert(arr);
assert(ret); assert(ret);
@ -731,14 +1307,14 @@ bool wm_ptrarray_pop_front(PtrArray *arr, void **ret)
if (arr->size == 0) if (arr->size == 0)
return false; return false;
*ret = arr->ptrs[0]; *ret = arr->elements[0];
arr->size--; arr->size--;
memmove(arr->ptrs, arr->ptrs+1, arr->size * sizeof(void*)); memmove(arr->elements, arr->elements+1, arr->size * sizeof(uint64_t));
return true; return true;
} }
bool wm_ptrarray_remove(PtrArray *arr, size_t index) bool wm_uintarray_remove(UIntArray *arr, size_t index)
{ {
DEBUG_PRINT("%s\n", __func__); DEBUG_PRINT("%s\n", __func__);
assert(arr); assert(arr);
@ -754,21 +1330,47 @@ bool wm_ptrarray_remove(PtrArray *arr, size_t index)
return true; return true;
} }
memmove(arr->ptrs + index, arr->ptrs + index+1, arr->size-1 * sizeof(void*)); memmove(arr->elements + index, arr->elements + index+1, arr->size-1 * sizeof(uint64_t));
return true; return true;
} }
void wm_ptrarray_free(PtrArray *arr) void wm_uintarray_free(UIntArray *arr)
{ {
// DEBUG_PRINT("%s\n", __func__); // DEBUG_PRINT("%s\n", __func__);
assert(arr); assert(arr);
RETURN_IF_NULL(arr->ptrs); RETURN_IF_NULL(arr->elements);
arr->capacity = 0; arr->capacity = 0;
arr->size = 0; arr->size = 0;
free(arr->ptrs); free(arr->elements);
arr->ptrs = NULL; arr->elements = NULL;
free(arr);
}
uint64_t wm_uintarray_2d_index(UIntArray *arr, size_t i, size_t j)
{
assert(arr);
return ((uint64_t**)arr->elements)[i][j];
}
void wm_logentries_free(UIntArray *entries)
{
for (size_t i = 0; i < entries->size; i++) {
LogEntry *entry = (LogEntry*)wm_uintarray_at(entries, i);
for (size_t j = 0; j < entry->workspaces->size; j++) {
TreeNode *node = entry->workspaces->nodes[j];
if (node != NULL) wm_treenode_free(node);
}
wm_nodearray_free(entry->workspaces);
free(entry);
}
wm_uintarray_free(entries);
} }
TreeNode* wm_treenode_ptr_find_focused_client_node(TreeNode *root) TreeNode* wm_treenode_ptr_find_focused_client_node(TreeNode *root)
@ -781,14 +1383,14 @@ TreeNode* wm_treenode_ptr_find_focused_client_node(TreeNode *root)
NodeArray *visited = wm_nodearray_new(); NodeArray *visited = wm_nodearray_new();
wm_nodearray_push(visited, root); wm_nodearray_push(visited, root);
PtrArray queue = wm_ptrarray_new(); NodeArray *queue = wm_nodearray_new();
wm_ptrarray_push(&queue, root); wm_nodearray_push(queue, root);
while (queue.size > 0) { while (queue->size > 0) {
TreeNode *node; TreeNode *node;
if (!wm_ptrarray_pop_front(&queue, (void**)&node)) if (!wm_nodearray_pop_front(queue, &node))
goto ret; goto ret;
if (node->type == NODE_CLIENT && node->client && node->client->focused) { if (node->type == NODE_CLIENT && node->client && node->client->focused) {
@ -809,14 +1411,14 @@ TreeNode* wm_treenode_ptr_find_focused_client_node(TreeNode *root)
if (!_visited) { if (!_visited) {
wm_nodearray_push(visited, child_node); wm_nodearray_push(visited, child_node);
wm_ptrarray_push(&queue, child_node); wm_nodearray_push(queue, child_node);
} }
} }
} }
ret: ret:
wm_nodearray_free(visited); wm_nodearray_free(visited);
wm_ptrarray_free(&queue); wm_nodearray_free(queue);
return ret; return ret;
} }

View File

@ -2,6 +2,7 @@
#define UTIL_H #define UTIL_H
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <json-c/json.h> #include <json-c/json.h>
@ -30,10 +31,10 @@ typedef struct {
} NodeArray; } NodeArray;
typedef struct { typedef struct {
void **ptrs; uint64_t *elements;
size_t size; size_t size;
size_t capacity; size_t capacity;
} PtrArray; } UIntArray;
typedef struct { typedef struct {
int x; int x;
@ -53,6 +54,7 @@ struct TreeNode {
Client *client; Client *client;
unsigned int id; unsigned int id;
uint64_t distance;
}; };
typedef struct { typedef struct {
@ -60,13 +62,30 @@ typedef struct {
char *str; char *str;
} WmWorkspaceToStrRet; } WmWorkspaceToStrRet;
typedef struct {
time_t timestamp;
bool after;
char function_name[128];
NodeArray *workspaces;
} LogEntry;
typedef uint64_t (*UpdateCostFunction)(TreeNode*, TreeNode*);
typedef struct {
uint64_t insert_cost;
uint64_t remove_cost;
UpdateCostFunction update_cost_function;
} TreeEditDistanceCosts;
NodeArray* wm_nodearray_new(); NodeArray* wm_nodearray_new();
void wm_nodearray_set(NodeArray *arr, size_t index, uint64_t value);
void wm_nodearray_push(NodeArray *arr, TreeNode *node); void wm_nodearray_push(NodeArray *arr, TreeNode *node);
bool wm_nodearray_pop(NodeArray *arr, TreeNode **ret); bool wm_nodearray_pop(NodeArray *arr, TreeNode **ret);
bool wm_nodearray_pop_front(NodeArray *arr, TreeNode **ret); bool wm_nodearray_pop_front(NodeArray *arr, TreeNode **ret);
void wm_nodearray_clear(NodeArray *arr); void wm_nodearray_clear(NodeArray *arr);
bool wm_nodearray_remove(NodeArray *arr, size_t index); bool wm_nodearray_remove(NodeArray *arr, size_t index);
void wm_nodearray_free(NodeArray *arr); void wm_nodearray_free(NodeArray *arr);
TreeNode* wm_nodearray_at(NodeArray *arr, size_t index);
TreeNode* wm_treenode_new(NodeType type, TreeNode *parent); TreeNode* wm_treenode_new(NodeType type, TreeNode *parent);
void wm_treenode_free(TreeNode *node); void wm_treenode_free(TreeNode *node);
@ -74,20 +93,29 @@ bool wm_treenode_is_empty(TreeNode *node);
void wm_treenode_split_space(TreeNode *node, Rect *ret1, Rect *ret2); void wm_treenode_split_space(TreeNode *node, Rect *ret1, Rect *ret2);
void wm_treenode_recalculate_space(TreeNode *node); void wm_treenode_recalculate_space(TreeNode *node);
void wm_treenode_swap(TreeNode *root, TreeNode *node1, TreeNode* node2); void wm_treenode_swap(TreeNode *root, TreeNode *node1, TreeNode* node2);
void wm_treenode_add_child(TreeNode *node, TreeNode *child);
TreeNode* wm_treenode_remove_client(Wm *wm, TreeNode *root, Client *client); TreeNode* wm_treenode_remove_client(Wm *wm, TreeNode *root, Client *client);
void wm_treenode_remove_node(Wm *wm, TreeNode *root, TreeNode *node); void wm_treenode_remove_node(Wm *wm, TreeNode *root, TreeNode *node);
int wm_get_node_index(TreeNode *parent, unsigned int node_id); int wm_get_node_index(TreeNode *parent, unsigned int node_id);
void wm_tree_to_DOT(TreeNode *root, const char *filename); void wm_tree_to_DOT(TreeNode *root, const char *filename);
void wm_node_type_to_str(NodeType type, char *buf, size_t bufsize); void wm_node_type_to_str(NodeType type, char *buf, size_t bufsize);
UIntArray* wm_nonempty_workspaces_to_strptrarray(Wm *wm);
NodeType wm_node_type_from_str(char *buf, size_t bufsize);
char* wm_treenode_to_str(TreeNode *node); char* wm_treenode_to_str(TreeNode *node);
void wm_treenode_print(TreeNode *node); void wm_treenode_print(TreeNode *node);
TreeNode* wm_json_obj_to_treenode(struct json_object *jobj);
UIntArray* wm_uintarray_new();
uint64_t wm_uintarray_at(UIntArray *arr, size_t index);
void wm_uintarray_set(UIntArray *arr, size_t index, uint64_t value);
void wm_uintarray_push(UIntArray *arr, uint64_t element);
bool wm_uintarray_pop(UIntArray *arr, uint64_t *ret);
bool wm_uintarray_pop_front(UIntArray *arr, uint64_t *ret);
bool wm_uintarray_remove(UIntArray *arr, size_t index);
void wm_uintarray_free(UIntArray *arr);
uint64_t wm_uintarray_2d_index(UIntArray *arr, size_t i, size_t j);
PtrArray wm_ptrarray_new();
void wm_ptrarray_push(PtrArray *arr, void* ptr);
bool wm_ptrarray_pop(PtrArray *arr, void **ret);
bool wm_ptrarray_pop_front(PtrArray *arr, void **ret);
bool wm_ptrarray_remove(PtrArray *arr, size_t index);
void wm_ptrarray_free(PtrArray *arr);
TreeNode* wm_treenode_ptr_find_focused_client_node(TreeNode *root); TreeNode* wm_treenode_ptr_find_focused_client_node(TreeNode *root);
TreeNode* wm_treenode_ptr_find_client_node(TreeNode *root, Client *client); TreeNode* wm_treenode_ptr_find_client_node(TreeNode *root, Client *client);
NodeArray* wm_treenode_find_client_nodes(TreeNode *root); NodeArray* wm_treenode_find_client_nodes(TreeNode *root);
@ -97,8 +125,28 @@ void wm_log_state(Wm *wm, const char *prefixstr, const char* logfile);
TreeNode* wm_treenode_lmd(TreeNode *node); TreeNode* wm_treenode_lmd(TreeNode *node);
NodeArray* wm_treenode_all_lmds(TreeNode *node); NodeArray* wm_treenode_all_lmds(TreeNode *node);
UIntArray* wm_treenode_all_lmds_index(TreeNode *node);
NodeArray* wm_postorder_traversal(TreeNode *tree); NodeArray* wm_postorder_traversal(TreeNode *tree);
bool wm_is_treenode_keyroot(TreeNode *node); bool wm_is_treenode_keyroot(TreeNode *node, NodeArray *postorder);
NodeArray* wm_treenode_all_keyroots(TreeNode *node); NodeArray* wm_treenode_all_keyroots(TreeNode *node);
UIntArray* wm_treenode_all_keyroots_index(TreeNode *node);
bool wm_nodes_are_equal(TreeNode *node1, TreeNode *node2);
void wm_treedist(TreeNode *tree1, TreeNode *tree2, size_t i, size_t j,
UIntArray *dists, TreeEditDistanceCosts costs);
size_t wm_tree_edit_distance(TreeNode *tree1, TreeNode *tree2, TreeEditDistanceCosts costs);
void wm_logfile_init(const char *path);
/* UIntArray<LogEntry*> */
void wm_logentries_free(UIntArray *entries);
/* UIntArray<LogEntry*> */
UIntArray* wm_read_log(const char* filename);
/* UIntArray<LogEntry*> */
void wm_logentries_calculate_distances(UIntArray *log_entries, TreeNode *curr_tree,
UpdateCostFunction update_cost_function);
#endif #endif

View File

@ -17,21 +17,21 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "wm.h" #include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <X11/X.h> #include <X11/X.h>
#include <X11/Xatom.h> #include <X11/Xatom.h>
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/Xutil.h> #include <X11/Xutil.h>
#include <X11/extensions/Xinerama.h> #include <X11/extensions/Xinerama.h>
#include <asm-generic/errno.h> #include "wm.h"
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <assert.h>
#include "config.h" #include "config.h"
#include "handler.h" #include "handler.h"
#include "client.h" #include "client.h"
@ -410,10 +410,11 @@ void wm_mainloop(Wm *wm)
void wm_init(Wm *wm) void wm_init(Wm *wm)
{ {
XSetWindowAttributes xa; DEBUG_PRINT("wm_init\n");
Display *d; DEBUG_PRINT("pid: %ld\n", getpid());
signal(SIGINT, wm_sigint_handler); XSetWindowAttributes xa;
// signal(SIGINT, wm_sigint_handler);
wm->config = (Config) {0}; wm->config = (Config) {0};
wm_cfg_init_def(&wm->config); wm_cfg_init_def(&wm->config);
@ -426,12 +427,11 @@ void wm_init(Wm *wm)
ConfigFile configfile = {0}; ConfigFile configfile = {0};
wm_configfile_init(&configfile, path); wm_configfile_init(&configfile, path);
wm_configfile_read(&configfile, &wm->config); wm_configfile_read(&configfile, &wm->config);
wm_logfile_init(wm->config.log_path);
wm->log_entries = wm_read_log(wm->config.log_path);
DEBUG_PRINT("wm_init\n") wm->display = wm_connect_display();
DEBUG_PRINT("pid: %ld\n", ((long)getpid())) wm_monitors_open_all(wm, wm->display);
d = wm_connect_display();
wm->display = d;
wm_monitors_open_all(wm, d);
char *display_string = DisplayString(wm->display); char *display_string = DisplayString(wm->display);
setenv("DISPLAY", display_string, 1); setenv("DISPLAY", display_string, 1);
@ -443,7 +443,7 @@ void wm_init(Wm *wm)
DEBUG_PRINT("root window: %ld\n", wm->root.window); DEBUG_PRINT("root window: %ld\n", wm->root.window);
XSync(d, false); XSync(wm->display, false);
xa.event_mask = StructureNotifyMask | StructureNotifyMask | xa.event_mask = StructureNotifyMask | StructureNotifyMask |
SubstructureNotifyMask | SubstructureRedirectMask | SubstructureNotifyMask | SubstructureRedirectMask |

View File

@ -67,16 +67,14 @@ if (c == NULL) \
#define WM_PREFIX_LEN 4096 #define WM_PREFIX_LEN 4096
#define WM_LOGFILENAME "./log" #define WM_LOG_STATE_START(wm) do { \
wm_log_state(wm, __func__, (wm)->config.log_path); \
#define WM_LOG_STATE_START(wm) do { \
wm_log_state(wm, __func__, WM_LOGFILENAME); \
} while (0); } while (0);
#define WM_LOG_STATE_END(wm) do { \ #define WM_LOG_STATE_END(wm) do { \
char __prefix[WM_PREFIX_LEN] = {0}; \ char __prefix[WM_PREFIX_LEN] = {0}; \
snprintf(__prefix, WM_PREFIX_LEN, "after %s", __func__); \ snprintf(__prefix, WM_PREFIX_LEN, "%s:after", __func__); \
wm_log_state(wm, __prefix, WM_LOGFILENAME); \ wm_log_state(wm, __prefix, (wm)->config.log_path); \
} while (0); } while (0);
typedef struct Client Client; typedef struct Client Client;
@ -140,6 +138,9 @@ struct Wm {
bool log_after_maprequest; bool log_after_maprequest;
int socket_fd; int socket_fd;
/* UIntArray<LogEntry*> */
UIntArray *log_entries;
}; };
Monitor wm_monitor_open(Display *d, XineramaScreenInfo info); Monitor wm_monitor_open(Display *d, XineramaScreenInfo info);