From fcdbafc3359c4f584491275d0bc404ccf9d8e98c Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Wed, 28 Jun 2023 16:56:39 +1000 Subject: [PATCH] introduce core-window.c core-window provides a window-core pane which provides core functionality for all windows. Some functionality is moved out of lib-input where it doesn't really belong. Signed-off-by: NeilBrown --- Makefile | 2 +- core-editor.c | 6 ++ core-window.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++ internal.h | 1 + lib-input.c | 112 --------------------------------- 5 files changed, 176 insertions(+), 113 deletions(-) create mode 100644 core-window.c diff --git a/Makefile b/Makefile index aac16c2f..8c718cf9 100644 --- a/Makefile +++ b/Makefile @@ -72,7 +72,7 @@ test: edlib lib shared test-rexel OBJ = O/edlib.o LIBOBJ = O/core-mark.o O/core-doc.o O/core-editor.o O/core-attr.o \ O/core-keymap.o O/core-pane.o O/core-misc.o O/core-log.o \ - O/core-version.o + O/core-version.o O/core-window.o SHOBJ = O/doc-text.o O/doc-dir.o O/doc-docs.o \ O/doc-email.o O/doc-multipart.o O/doc-list.o \ O/render-hex.o O/render-lines.o \ diff --git a/core-editor.c b/core-editor.c index 26b51608..4b1ca01b 100644 --- a/core-editor.c +++ b/core-editor.c @@ -232,6 +232,11 @@ DEF_CMD(editor_activate_display) ip = strsave(ci->home, ip); p = pane_root(ci->focus); + p2 = call_ret(pane, "attach-window-core", p); + if (!p2) + return Efail; + p = p2; + for (t = strtok_r(ip, " \t\n", &save); t; t = strtok_r(NULL, " \t\n", &save)) { @@ -496,6 +501,7 @@ struct pane *editor_new(void) if (ed) { doc_setup(ed); log_setup(ed); + window_setup(ed); } return ed; } diff --git a/core-window.c b/core-window.c new file mode 100644 index 00000000..058b36ce --- /dev/null +++ b/core-window.c @@ -0,0 +1,168 @@ +/* + * Copyright Neil Brown ©2023 + * May be distributed under terms of GPLv2 - see file:COPYING + * + * Core per-window functionality. + * + * Provide a pane that is instantiated between the root and any window + * stack, to provide common functionality. These includes: + * + * - registering and forwarding per-window notifications + * - Being an intermediary for per-window selections. + * + * ============================================================== + * Allow any pane to "claim ownership" of "the selection", or to + * "commit" the selection. A pane can also "discard" the selection, + * but that only works if the pane owns it. + * + * This can be used for mouse-based copy/paste and interaction with the + * X11 "PRIMARY" clipboard. + * When a selection is made in any pane it claims "the selection". + * When a mouse-based paste request is made, the receiving pane can ask for + * the selection to be "commited", and then access the most recent copy-buffer. + * The owner of a selection will, if the selection is still valid, call + * copy:save to save the selected content. + * When a "paste" request is made where the location is based on the "point" + * (current cursor) it is unlikely that a selection in the same pane should be + * used - if there is one it is more likely to be intended to receive the paste. + * So the target pane can first "discard" the selection, then "commit", then call + * "copy:get". If the selection is in this pane, the "discard" will succeed, + * the "commit" will be a no-op, and the top copy buf will be used. + * If the selection is in another pane (or another app via X11), the "discard" + * will fail (wrong owner), the "commit" will succeed and copy the selection, + * and the "copy:get" will get it. + * + * Operations are "selection:claim", "selection:commit" and "selection:discard". + * When the selection is claimed, the old owner gets called (not notified) + * "Notify:selection:claimed", and when a commit request is made, + * "Notify:selection:commit" is sent. + * + * A client can declare itself to be a fall-back handler by calling + * select:claim with num==1. Then if any other client discards its selection, + * the ownership reverse to the fallback. The fallback typically provides + * access to some selection external to edlib, such as the x11 selections. + */ +#include +#include +#include +#include +#include + +#include "core.h" +#include "internal.h" + +struct window_data { + struct pane *sel_owner; + int sel_committed; + struct pane *sel_owner_fallback; +}; + +DEF_CMD(request_notify) +{ + pane_add_notify(ci->focus, ci->home, ksuffix(ci, "window:request:")); + return 1; +} + +DEF_CMD(send_notify) +{ + /* window:notify:... */ + return home_pane_notify(ci->home, ksuffix(ci, "window:notify:"), + ci->focus, + ci->num, ci->mark, ci->str, + ci->num2, ci->mark2, ci->str2, ci->comm2); +} + +DEF_CMD(selection_claim) +{ + struct window_data *wd = ci->home->data; + + if (wd->sel_owner && wd->sel_owner != ci->focus) { + call("Notify:selection:claimed", wd->sel_owner); + //pane_drop_notifiers(ci->home, "Notify:Close", wd->sel_owner); + } + wd->sel_owner = ci->focus; + if (ci->num == 1) + wd->sel_owner_fallback = ci->focus; + wd->sel_committed = 0; + pane_add_notify(ci->home, ci->focus, "Notify:Close"); + return 1; +} + +DEF_CMD(selection_commit) +{ + struct window_data *wd = ci->home->data; + + if (wd->sel_owner && !wd->sel_committed) { + if (call("Notify:selection:commit", wd->sel_owner) != 2) + wd->sel_committed = 1; + } + return 1; +} + +DEF_CMD(selection_discard) +{ + struct window_data *wd = ci->home->data; + struct pane *op, *fp; + + if (!wd->sel_owner) + return Efalse; + if (wd->sel_owner_fallback == ci->focus) + wd->sel_owner_fallback = NULL; + /* Don't require exactly same pane for sel_owner, + * but ensure they have the same focus. + */ + op = pane_leaf(wd->sel_owner); + fp = pane_leaf(ci->focus); + if (fp != op) + return Efalse; + + wd->sel_owner = wd->sel_owner_fallback; + wd->sel_committed = 0; + return 1; +} + +DEF_CMD(close_notify) +{ + struct window_data *wd = ci->home->data; + + if (wd->sel_owner_fallback == ci->focus) + wd->sel_owner_fallback = NULL; + + if (wd->sel_owner == ci->focus) + wd->sel_owner = wd->sel_owner_fallback; + return 1; +} + +static struct map *window_map; +DEF_LOOKUP_CMD(window_handle, window_map); + +DEF_CMD(window_attach) +{ + struct window_data *wd; + struct pane *p; + + alloc(wd, pane); + p = pane_register(ci->focus, 0, &window_handle.c, wd); + if (!p) { + unalloc(wd, pane); + return Efail; + } + comm_call(ci->comm2, "cb", p); + return 1; +} + +void window_setup(struct pane *ed safe) +{ + window_map = key_alloc(); + + key_add_prefix(window_map, "window:request:", &request_notify); + key_add_prefix(window_map, "window:notify:", &send_notify); + + key_add(window_map, "selection:claim", &selection_claim); + key_add(window_map, "selection:commit", &selection_commit); + key_add(window_map, "selection:discard", &selection_discard); + key_add(window_map, "Notify:Close", &close_notify); + + call_comm("global-set-command", ed, &window_attach, 0, NULL, + "attach-window-core"); +} diff --git a/internal.h b/internal.h index 903f773d..a9d3dfd0 100644 --- a/internal.h +++ b/internal.h @@ -23,3 +23,4 @@ void editor_delayed_free(struct pane *ed safe, struct pane *p safe); void editor_delayed_mark_free(struct mark *m safe); void doc_setup(struct pane *ed safe); void log_setup(struct pane *ed safe); +void window_setup(struct pane *ed safe); diff --git a/lib-input.c b/lib-input.c index 1ef85250..3ccbce3d 100644 --- a/lib-input.c +++ b/lib-input.c @@ -6,38 +6,6 @@ * This module transalates keystrokes and mouse events into commands. * This involves tracking the current 'mode' state. * - * ============================================================== - * This might belong in a separate pane/module but for now also: - * Allow any pane to "claim ownership" of "the selection", or to - * "commit" the selection. A pane can also "discard" the selection, - * but that only works if the pane owns it. - * - * This can be used for mouse-based copy/paste and interaction with the - * X11 "PRIMARY" clipboard. - * When a selection is made in any pane it claims "the selection". - * When a mouse-based paste request is made, the receiving pane can ask for - * the selection to be "commited", and then access the most recent copy-buffer. - * The owner of a selection will, if the selection is still valid, call - * copy:save to save the selected content. - * When a "paste" request is made where the location is based on the "point" - * (current cursor) it is unlikely that a selection in the same pane should be - * used - if there is one it is more likely to be intended to receive the paste. - * So the target pane can first "discard" the selection, then "commit", then call - * "copy:get". If the selection is in this pane, the "discard" will succeed, - * the "commit" will be a no-op, and the top copy buf will be used. - * If the selection is in another pane (or another app via X11), the "discard" - * will fail (wrong owner), the "commit" will succeed and copy the selection, - * and the "copy:get" will get it. - * - * Operations are "selection:claim", "selection:commit" and "selection:discard". - * When the selection is claimed, the old owner gets called (not notified) - * "Notify:selection:claimed", and when a commit request is made, - * "Notify:selection:commit" is sent. - * - * A client can declare itself to be a fall-back handler by calling - * select:claim with num==1. Then if any other client discards its selection, - * the ownership reverse to the fallback. The fallback typically provides - * access to some selection external to edlib, such as the x11 selections. */ #define _GNU_SOURCE /* for asprintf */ @@ -66,10 +34,6 @@ struct input_mode { char *log[LOGSIZE]; int head; - - struct pane *sel_owner; - int sel_committed; - struct pane *sel_owner_fallback; }; /* 'head' is 1 more than the last key added. */ @@ -447,21 +411,6 @@ DEF_CMD(mouse_event) return Efalse; } -DEF_CMD(request_notify) -{ - pane_add_notify(ci->focus, ci->home, ksuffix(ci, "window:request:")); - return 1; -} - -DEF_CMD(send_notify) -{ - /* window:notify:... */ - return home_pane_notify(ci->home, ksuffix(ci, "window:notify:"), - ci->focus, - ci->num, ci->mark, ci->str, - ci->num2, ci->mark2, ci->str2, ci->comm2); -} - DEF_CMD(refocus) { struct input_mode *im = ci->home->data; @@ -482,66 +431,11 @@ DEF_CMD(close_focus) im->source = NULL; } - if (im->sel_owner_fallback == ci->focus) - im->sel_owner_fallback = NULL; - - if (im->sel_owner == ci->focus) - im->sel_owner = im->sel_owner_fallback; - if (im->grab == ci->focus) im->grab = NULL; return 1; } -DEF_CMD(selection_claim) -{ - struct input_mode *im = ci->home->data; - - if (im->sel_owner && im->sel_owner != ci->focus) { - call("Notify:selection:claimed", im->sel_owner); - //pane_drop_notifiers(ci->home, "Notify:Close", im->sel_owner); - } - im->sel_owner = ci->focus; - if (ci->num == 1) - im->sel_owner_fallback = ci->focus; - im->sel_committed = 0; - pane_add_notify(ci->home, ci->focus, "Notify:Close"); - return 1; -} - -DEF_CMD(selection_commit) -{ - struct input_mode *im = ci->home->data; - - if (im->sel_owner && !im->sel_committed) { - if (call("Notify:selection:commit", im->sel_owner) != 2) - im->sel_committed = 1; - } - return 1; -} - -DEF_CMD(selection_discard) -{ - struct input_mode *im = ci->home->data; - struct pane *op, *fp; - - if (!im->sel_owner) - return Efalse; - if (im->sel_owner_fallback == ci->focus) - im->sel_owner_fallback = NULL; - /* Don't require exactly same pane for sel_owner, - * but ensure they have the same focus. - */ - op = pane_leaf(im->sel_owner); - fp = pane_leaf(ci->focus); - if (fp != op) - return Efalse; - - im->sel_owner = im->sel_owner_fallback; - im->sel_committed = 0; - return 1; -} - DEF_CMD(input_free) { struct input_mode *im = ci->home->data; @@ -570,13 +464,7 @@ static void register_map(void) key_add(im_map, "pane:refocus", &refocus); key_add(im_map, "Notify:Close", &close_focus); key_add(im_map, "input:log", &log_input); - key_add_prefix(im_map, "window:request:", &request_notify); - key_add_prefix(im_map, "window:notify:", &send_notify); key_add(im_map, "Free", &input_free); - - key_add(im_map, "selection:claim", &selection_claim); - key_add(im_map, "selection:commit", &selection_commit); - key_add(im_map, "selection:discard", &selection_discard); } DEF_LOOKUP_CMD(input_handle, im_map); -- 2.39.5