2 * Copyright Neil Brown ©2023 <neil@brown.name>
3 * May be distributed under terms of GPLv2 - see file:COPYING
5 * Core per-window functionality.
7 * Provide a pane that is instantiated between the root and any window
8 * stack, to provide common functionality. These includes:
10 * - registering and forwarding per-window notifications
11 * - Being an intermediary for per-window selections.
13 * ==============================================================
14 * Allow any pane to "claim ownership" of "the selection", or to
15 * "commit" the selection. A pane can also "discard" the selection,
16 * but that only works if the pane owns it.
18 * This can be used for mouse-based copy/paste and interaction with the
19 * X11 "PRIMARY" clipboard.
20 * When a selection is made in any pane it claims "the selection".
21 * When a mouse-based paste request is made, the receiving pane can ask for
22 * the selection to be "commited", and then access the most recent copy-buffer.
23 * The owner of a selection will, if the selection is still valid, call
24 * copy:save to save the selected content.
25 * When a "paste" request is made where the location is based on the "point"
26 * (current cursor) it is unlikely that a selection in the same pane should be
27 * used - if there is one it is more likely to be intended to receive the paste.
28 * So the target pane can first "discard" the selection, then "commit", then call
29 * "copy:get". If the selection is in this pane, the "discard" will succeed,
30 * the "commit" will be a no-op, and the top copy buf will be used.
31 * If the selection is in another pane (or another app via X11), the "discard"
32 * will fail (wrong owner), the "commit" will succeed and copy the selection,
33 * and the "copy:get" will get it.
35 * Operations are "selection:claim", "selection:commit" and "selection:discard".
36 * When the selection is claimed, the old owner gets called (not notified)
37 * "Notify:selection:claimed", and when a commit request is made,
38 * "Notify:selection:commit" is sent.
40 * A client can declare itself to be a fall-back handler by calling
41 * select:claim with num==1. Then if any other client discards its selection,
42 * the ownership reverse to the fallback. The fallback typically provides
43 * access to some selection external to edlib, such as the x11 selections.
55 struct pane *sel_owner;
57 struct pane *sel_owner_fallback;
60 DEF_CMD(request_notify)
62 pane_add_notify(ci->focus, ci->home, ksuffix(ci, "window:request:"));
68 /* window:notify:... */
69 return home_pane_notify(ci->home, ksuffix(ci, "window:notify:"),
71 ci->num, ci->mark, ci->str,
72 ci->num2, ci->mark2, ci->str2, ci->comm2);
75 DEF_CMD(selection_claim)
77 struct window_data *wd = ci->home->data;
79 if (wd->sel_owner && wd->sel_owner != ci->focus) {
80 call("Notify:selection:claimed", wd->sel_owner);
81 //pane_drop_notifiers(ci->home, "Notify:Close", wd->sel_owner);
83 wd->sel_owner = ci->focus;
85 wd->sel_owner_fallback = ci->focus;
86 wd->sel_committed = 0;
87 pane_add_notify(ci->home, ci->focus, "Notify:Close");
91 DEF_CMD(selection_commit)
93 struct window_data *wd = ci->home->data;
95 if (wd->sel_owner && !wd->sel_committed) {
96 if (call("Notify:selection:commit", wd->sel_owner) != 2)
97 wd->sel_committed = 1;
102 DEF_CMD(selection_discard)
104 struct window_data *wd = ci->home->data;
105 struct pane *op, *fp;
109 if (wd->sel_owner_fallback == ci->focus)
110 wd->sel_owner_fallback = NULL;
111 /* Don't require exactly same pane for sel_owner,
112 * but ensure they have the same focus.
114 op = pane_leaf(wd->sel_owner);
115 fp = pane_leaf(ci->focus);
119 wd->sel_owner = wd->sel_owner_fallback;
120 wd->sel_committed = 0;
124 DEF_CMD(close_notify)
126 struct window_data *wd = ci->home->data;
128 if (wd->sel_owner_fallback == ci->focus)
129 wd->sel_owner_fallback = NULL;
131 if (wd->sel_owner == ci->focus)
132 wd->sel_owner = wd->sel_owner_fallback;
136 static struct map *window_map;
137 DEF_LOOKUP_CMD(window_handle, window_map);
139 DEF_CMD(window_attach)
141 struct window_data *wd;
145 p = pane_register(ci->focus, 0, &window_handle.c, wd);
150 comm_call(ci->comm2, "cb", p);
154 void window_setup(struct pane *ed safe)
156 window_map = key_alloc();
158 key_add_prefix(window_map, "window:request:", &request_notify);
159 key_add_prefix(window_map, "window:notify:", &send_notify);
161 key_add(window_map, "selection:claim", &selection_claim);
162 key_add(window_map, "selection:commit", &selection_commit);
163 key_add(window_map, "selection:discard", &selection_discard);
164 key_add(window_map, "Notify:Close", &close_notify);
166 call_comm("global-set-command", ed, &window_attach, 0, NULL,
167 "attach-window-core");