diff --git a/src/newifc/genapi.c b/src/newifc/genapi.c index f7b34c1e52f837a3f9977ed561b45d576c41a7fd..cc324be5cda77862f30a6ca012925fd05a28cbdc 100644 --- a/src/newifc/genapi.c +++ b/src/newifc/genapi.c @@ -45,6 +45,7 @@ enum attribute_impl { attr_impl_global, attr_impl_object, attr_impl_root, + attr_impl_global_custom_setter, }; struct attribute_info { @@ -62,7 +63,9 @@ attributes[] = { {"child_xpos", NI_attr_type_uint16_t, attr_impl_global, 1}, {"child_ypos", NI_attr_type_uint16_t, attr_impl_global, 1}, {"dirty", NI_attr_type_bool, attr_impl_root, 1}, + {"focus", NI_attr_type_bool, attr_impl_global_custom_setter, 0}, {"height", NI_attr_type_uint16_t, attr_impl_global, 0}, + {"hidden", NI_attr_type_bool, attr_impl_global, 1}, {"higherpeer", NI_attr_type_NewIfcObj, attr_impl_global, 1}, {"last_error", NI_attr_type_NI_err, attr_impl_global, 1}, {"locked", NI_attr_type_bool, attr_impl_root, 0}, @@ -98,6 +101,7 @@ error_inf[] = { {"error_out_of_range", -1}, {"error_wont_fit", -1}, {"error_cancelled", -1}, + {"error_skip_subtree", -1}, }; struct handler_info { @@ -157,12 +161,14 @@ main(int argc, char **argv) fputs("NI_err NI_copy(NewIfcObj obj, NewIfcObj *newobj);\n", header); fputs("NI_err NI_create(enum NewIfc_object obj, NewIfcObj parent, NewIfcObj *newobj);\n", header); fputs("NI_err NI_error(NewIfcObj obj);\n", header); - fputs("NI_err NI_walk_children(NewIfcObj obj, NI_err (*cb)(NewIfcObj obj, void *cb_data), void *cbdata);\n\n", header); + fputs("NI_err NI_walk_children(NewIfcObj obj, bool top_down, NI_err (*cb)(NewIfcObj obj, void *cb_data), void *cbdata);\n\n", header); nitems = sizeof(attributes) / sizeof(attributes[0]); for (i = 0; i < nitems; i++) { - if (!attributes[i].read_only) + if (!attributes[i].read_only) { + fprintf(header, "NI_err NI_add_%s_handler(NewIfcObj obj, NI_err (*handler)(NewIfcObj obj, %s newval, void *cbdata), void *cbdata);\n", attributes[i].name, type_str[attributes[i].type].type); fprintf(header, "NI_err NI_set_%s(NewIfcObj obj, %s value);\n", attributes[i].name, type_str[attributes[i].type].type); + } fprintf(header, "NI_err NI_get_%s(NewIfcObj obj, %s* value);\n", attributes[i].name, type_str[attributes[i].type].type); } fputs("\n#endif\n", header); @@ -212,7 +218,7 @@ main(int argc, char **argv) " NI_err (*set)(NewIfcObj niobj, const int attr, ...);\n" " NI_err (*get)(NewIfcObj niobj, const int attr, ...);\n" " NI_err (*copy)(NewIfcObj obj, NewIfcObj *newobj);\n" - " struct NewIfc_handler *handlers;\n" + " struct NewIfc_handler **handlers;\n" " size_t handlers_sz;\n" " NewIfcObj root;\n" " NewIfcObj parent;\n" @@ -232,6 +238,8 @@ main(int argc, char **argv) " uint16_t child_width;\n" " uint16_t child_xpos;\n" " uint16_t child_ypos;\n" + " unsigned focus:1;\n" + " unsigned hidden:1;\n" "};\n\n", internal_header); fputs("enum NewIfc_attribute {\n", internal_header); @@ -325,9 +333,10 @@ main(int argc, char **argv) "{\n" " if (obj->handlers == NULL)\n" " return NewIfc_error_none;\n" - " struct NewIfc_handler *h = bsearch(&type, obj->handlers, obj->handlers_sz, sizeof(obj->handlers[0]), handler_compar);\n" - " if (h == NULL)\n" + " struct NewIfc_handler **head = bsearch(&type, obj->handlers, obj->handlers_sz, sizeof(obj->handlers[0]), handler_bsearch_compar);\n" + " if (head == NULL)\n" " return NewIfc_error_none;\n" + " struct NewIfc_handler *h = *head;\n" " while (h != NULL) {\n" " NI_err ret = h->on_%s_change(obj, newval, h->cbdata);\n" " if (ret != NewIfc_error_none)\n" @@ -398,6 +407,8 @@ main(int argc, char **argv) "}\n\n", attributes[i].name, type_str[attributes[i].type].type, attributes[i].name, attributes[i].name); } + // Fall-through + case attr_impl_global_custom_setter: fprintf(c_code, "NI_err\n" "NI_get_%s(NewIfcObj obj, %s* value) {\n" " NI_err ret;\n" @@ -442,6 +453,30 @@ main(int argc, char **argv) "}\n\n", attributes[i].name, type_str[attributes[i].type].type, attributes[i].name); break; } + fprintf(c_code, "NI_err\n" + "NI_add_%s_handler(NewIfcObj obj, NI_err (*handler)(NewIfcObj obj, %s newval, void *cbdata), void *cbdata)\n" + "{\n" + " NI_err ret;\n" + " if (obj == NULL || handler == NULL)\n" + " return NewIfc_error_invalid_arg;\n" + " if (NI_set_locked(obj, true)) {\n" + " struct NewIfc_handler *h = malloc(sizeof(struct NewIfc_handler));\n" + " if (h == NULL)\n" + " return NewIfc_error_allocation_failure;\n" + " h->on_%s_change = handler;\n" + " h->cbdata = cbdata;\n" + " h->event = NewIfc_on_%s_change;\n" + " h->next = NULL;\n" + " ret = NI_install_handler(obj, h);\n" + " NI_set_locked(obj, false);\n" + " }\n" + " else\n" + " ret = NewIfc_error_lock_failed;\n" + " return ret;\n" + "}\n\n", attributes[i].name, type_str[attributes[i].type].type, type_str[attributes[i].type].var_name, attributes[i].name); } + + fputs("#include \"newifc_nongen_after.c\"\n\n", c_code); + fclose(c_code); } diff --git a/src/newifc/newifc_nongen.c b/src/newifc/newifc_nongen.c index bf290feeec4abda817a8b2c230da7ac85985fc55..b9c7a861f12f1c09648c16a904a83845766a9315 100644 --- a/src/newifc/newifc_nongen.c +++ b/src/newifc/newifc_nongen.c @@ -28,27 +28,41 @@ NI_copy(NewIfcObj obj, NewIfcObj *newobj) { } static NI_err -NI_walk_children_recurse(NewIfcObj obj, NI_err (*cb)(NewIfcObj obj, void *cb_data), void *cbdata) +NI_walk_children_recurse(NewIfcObj obj, bool top_down, NI_err (*cb)(NewIfcObj obj, void *cb_data), void *cbdata) { NI_err err; + NewIfcObj nobj; if (!obj) return NewIfc_error_none; err = cb(obj, cbdata); if (err != NewIfc_error_none) return err; - if (obj->bottomchild != NULL) { - err = NI_walk_children_recurse(obj->bottomchild, cb, cbdata); - if (err != NewIfc_error_none) + if (top_down) + nobj = obj->topchild; + else + nobj = obj->bottomchild; + + if (nobj != NULL) { + err = NI_walk_children_recurse(nobj, top_down, cb, cbdata); + if (err != NewIfc_error_none && err != NewIfc_error_skip_subtree) return err; } - if (!obj->higherpeer) + if (top_down) + nobj = obj->lowerpeer; + else + nobj = obj->higherpeer; + + if (!nobj) return NewIfc_error_none; - return NI_walk_children_recurse(obj->higherpeer, cb, cbdata); + err = NI_walk_children_recurse(nobj, top_down, cb, cbdata); + if (err != NewIfc_error_none && err != NewIfc_error_skip_subtree) + return err; + return NewIfc_error_none; } NI_err -NI_walk_children(NewIfcObj obj, NI_err (*cb)(NewIfcObj obj, void *cb_data), void *cbdata) +NI_walk_children(NewIfcObj obj, bool top_down, NI_err (*cb)(NewIfcObj obj, void *cb_data), void *cbdata) { NI_err ret; @@ -58,7 +72,7 @@ NI_walk_children(NewIfcObj obj, NI_err (*cb)(NewIfcObj obj, void *cb_data), void return NewIfc_error_invalid_arg; ret = NI_set_locked(obj, true); if (ret == NewIfc_error_none) { - ret = NI_walk_children_recurse(obj->bottomchild, cb, cbdata); + ret = NI_walk_children_recurse(obj->bottomchild, top_down, cb, cbdata); NI_set_locked(obj, false); } else @@ -66,15 +80,59 @@ NI_walk_children(NewIfcObj obj, NI_err (*cb)(NewIfcObj obj, void *cb_data), void return ret; } -int -handler_compar(const void *a, const void *b) +static int +handler_bsearch_compar(const void *a, const void *b) { const enum NewIfc_event_handlers *key = a; - const struct NewIfc_handler *tst = b; + const struct NewIfc_handler **tst = (const struct NewIfc_handler **)b; - if (*key < tst->event) + if (*key < (*tst)->event) return -1; - if (*key > tst->event) + if (*key > (*tst)->event) return 1; return 0; } + +static int +handler_qsort_compar(const void *a, const void *b) +{ + const struct NewIfc_handler **ap = (const struct NewIfc_handler **)a; + const struct NewIfc_handler **bp = (const struct NewIfc_handler **)b; + + return ((*ap)->event - (*bp)->event); +} + +static NI_err +NI_install_handler(NewIfcObj obj, struct NewIfc_handler *handler) +{ + struct NewIfc_handler **head = bsearch(&handler->event, obj->handlers, + obj->handlers_sz, sizeof(obj->handlers[0]), handler_bsearch_compar); + + if (head != NULL) { + handler->next = *head; + *head = handler; + return NewIfc_error_none; + } + // Increase the size of handlers... + // TODO: this is pretty inefficient, a bsearch() for insert point + // would be best, but even a linear search for it would likely be + // better. + size_t new_sz = obj->handlers_sz; + struct NewIfc_handler ***na = realloc(obj->handlers, new_sz * sizeof(obj->handlers[0])); + if (na == NULL) + return NewIfc_error_allocation_failure; + obj->handlers[obj->handlers_sz] = handler; + obj->handlers_sz = new_sz; + qsort(obj->handlers, obj->handlers_sz, sizeof(obj->handlers[0]), handler_qsort_compar); + return NewIfc_error_none; +} + +static NI_err +remove_focus_cb(NewIfcObj obj, void *cbdata) +{ + (void)cbdata; + if (obj->focus == false) + return NewIfc_error_skip_subtree; + obj->set(obj, NewIfc_focus, false); + return NewIfc_error_none; +} diff --git a/src/newifc/root_window.c b/src/newifc/root_window.c index f92cab99be2a51fefba960ce992331a6dc11b727..36d40332bc086d0578b6899d8b15ec29008b901a 100644 --- a/src/newifc/root_window.c +++ b/src/newifc/root_window.c @@ -63,7 +63,7 @@ rw_recalc_child(struct root_window *rw, uint16_t height, uint16_t width) rw->api.last_error = NewIfc_error_wont_fit; return; } - if (NI_walk_children((NewIfcObj)rw, rw_recalc_child_cb, &nsz) != NewIfc_error_none) { + if (NI_walk_children((NewIfcObj)rw, false, rw_recalc_child_cb, &nsz) != NewIfc_error_none) { rw->api.last_error = NewIfc_error_wont_fit; return; } @@ -242,6 +242,7 @@ NewIFC_root_window(NewIfcObj *newobj) (*newrw)->api.min_width = 40; // TODO: This is only needed by the unit tests... (*newrw)->api.root = *newobj; + (*newrw)->api.focus = true; (*newrw)->show_title = true; (*newrw)->help = true; (*newrw)->title_sz = 0; @@ -276,6 +277,7 @@ void test_root_window(CuTest *ct) CuAssertTrue(ct, obj->min_width == 40); CuAssertTrue(ct, obj->min_height == 2); CuAssertTrue(ct, obj->last_error == NewIfc_error_none); + CuAssertTrue(ct, obj->focus == true); CuAssertTrue(ct, obj->get(obj, NewIfc_show_title, &b) == NewIfc_error_none && b); CuAssertTrue(ct, obj->last_error == NewIfc_error_none); CuAssertTrue(ct, obj->get(obj, NewIfc_show_help, &b) == NewIfc_error_none && b);