]> git.neil.brown.name Git - edlib.git/blob - core-window.c
introduce core-window.c
[edlib.git] / core-window.c
1 /*
2  * Copyright Neil Brown ©2023 <neil@brown.name>
3  * May be distributed under terms of GPLv2 - see file:COPYING
4  *
5  * Core per-window functionality.
6  *
7  * Provide a pane that is instantiated between the root and any window
8  * stack, to provide common functionality.  These includes:
9  *
10  * - registering and forwarding per-window notifications
11  * - Being an intermediary for per-window selections.
12  *
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.
17  *
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.
34  *
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.
39  *
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.
44  */
45 #include <unistd.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <time.h>
49 #include <stdio.h>
50
51 #include "core.h"
52 #include "internal.h"
53
54 struct window_data {
55         struct pane     *sel_owner;
56         int             sel_committed;
57         struct pane     *sel_owner_fallback;
58 };
59
60 DEF_CMD(request_notify)
61 {
62         pane_add_notify(ci->focus, ci->home, ksuffix(ci, "window:request:"));
63         return 1;
64 }
65
66 DEF_CMD(send_notify)
67 {
68         /* window:notify:... */
69         return home_pane_notify(ci->home, ksuffix(ci, "window:notify:"),
70                                 ci->focus,
71                                 ci->num, ci->mark, ci->str,
72                                 ci->num2, ci->mark2, ci->str2, ci->comm2);
73 }
74
75 DEF_CMD(selection_claim)
76 {
77         struct window_data *wd = ci->home->data;
78
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);
82         }
83         wd->sel_owner = ci->focus;
84         if (ci->num == 1)
85                 wd->sel_owner_fallback = ci->focus;
86         wd->sel_committed = 0;
87         pane_add_notify(ci->home, ci->focus, "Notify:Close");
88         return 1;
89 }
90
91 DEF_CMD(selection_commit)
92 {
93         struct window_data *wd = ci->home->data;
94
95         if (wd->sel_owner && !wd->sel_committed) {
96                 if (call("Notify:selection:commit", wd->sel_owner) != 2)
97                         wd->sel_committed = 1;
98         }
99         return 1;
100 }
101
102 DEF_CMD(selection_discard)
103 {
104         struct window_data *wd = ci->home->data;
105         struct pane *op, *fp;
106
107         if (!wd->sel_owner)
108                 return Efalse;
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.
113          */
114         op = pane_leaf(wd->sel_owner);
115         fp = pane_leaf(ci->focus);
116         if (fp != op)
117                 return Efalse;
118
119         wd->sel_owner = wd->sel_owner_fallback;
120         wd->sel_committed = 0;
121         return 1;
122 }
123
124 DEF_CMD(close_notify)
125 {
126         struct window_data *wd = ci->home->data;
127
128         if (wd->sel_owner_fallback == ci->focus)
129                 wd->sel_owner_fallback = NULL;
130
131         if (wd->sel_owner == ci->focus)
132                 wd->sel_owner = wd->sel_owner_fallback;
133         return 1;
134 }
135
136 static struct map *window_map;
137 DEF_LOOKUP_CMD(window_handle, window_map);
138
139 DEF_CMD(window_attach)
140 {
141         struct window_data *wd;
142         struct pane *p;
143
144         alloc(wd, pane);
145         p = pane_register(ci->focus, 0, &window_handle.c, wd);
146         if (!p) {
147                 unalloc(wd, pane);
148                 return Efail;
149         }
150         comm_call(ci->comm2, "cb", p);
151         return 1;
152 }
153
154 void window_setup(struct pane *ed safe)
155 {
156         window_map = key_alloc();
157
158         key_add_prefix(window_map, "window:request:", &request_notify);
159         key_add_prefix(window_map, "window:notify:", &send_notify);
160
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);
165
166         call_comm("global-set-command", ed, &window_attach, 0, NULL,
167                   "attach-window-core");
168 }