]> git.neil.brown.name Git - edlib.git/blob - core-window.c
tile: don't use "Free", just "Close".
[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  * - setting per-window attributes
11  * - registering and forwarding per-window notifications
12  * - Being an intermediary for per-window selections.
13  *
14  * ==============================================================
15  * Allow any pane to "claim ownership" of "the selection", or to
16  * "commit" the selection.  A pane can also "discard" the selection,
17  * but that only works if the pane owns it.
18  *
19  * This can be used for mouse-based copy/paste and interaction with the
20  * X11 "PRIMARY" clipboard.
21  * When a selection is made in any pane it claims "the selection".
22  * When a mouse-based paste request is made, the receiving pane can ask for
23  * the selection to be "commited", and then access the most recent copy-buffer.
24  * The owner of a selection will, if the selection is still valid, call
25  * copy:save to save the selected content.
26  * When a "paste" request is made where the location is based on the "point"
27  * (current cursor) it is unlikely that a selection in the same pane should be
28  * used - if there is one it is more likely to be intended to receive the paste.
29  * So the target pane can first "discard" the selection, then "commit", then call
30  * "copy:get".  If the selection is in this pane, the "discard" will succeed,
31  * the "commit" will be a no-op, and the top copy buf will be used.
32  * If the selection is in another pane (or another app via X11), the "discard"
33  * will fail (wrong owner), the "commit" will succeed and copy the selection,
34  * and the "copy:get" will get it.
35  *
36  * Operations are "selection:claim", "selection:commit" and "selection:discard".
37  * When the selection is claimed, the old owner gets called (not notified)
38  * "Notify:selection:claimed", and when a commit request is made,
39  * "Notify:selection:commit" is sent.
40  *
41  * A client can declare itself to be a fall-back handler by calling
42  * select:claim with num==1.  Then if any other client discards its selection,
43  * the ownership reverse to the fallback.  The fallback typically provides
44  * access to some selection external to edlib, such as the x11 selections.
45  */
46 #include <unistd.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <time.h>
50 #include <stdio.h>
51
52 #define PANE_DATA_TYPE struct window_data
53 #include "core.h"
54 #include "internal.h"
55
56 struct window_data {
57         struct pane     *sel_owner;
58         int             sel_committed;
59         struct pane     *sel_owner_fallback;
60 };
61 #include "core-pane.h"
62
63 DEF_CMD(request_notify)
64 {
65         pane_add_notify(ci->focus, ci->home, ksuffix(ci, "window:request:"));
66         return 1;
67 }
68
69 DEF_CMD(send_notify)
70 {
71         /* window:notify:... */
72         return home_pane_notify(ci->home, ksuffix(ci, "window:notify:"),
73                                 ci->focus,
74                                 ci->num, ci->mark, ci->str,
75                                 ci->num2, ci->mark2, ci->str2, ci->comm2);
76 }
77
78 DEF_CMD(window_set)
79 {
80         const char *val = ksuffix(ci, "window:set:");
81
82         if (!*val)
83                 val = ci->str2;
84         if (!val)
85                 return Enoarg;
86
87         if (ci->str)
88                 attr_set_str(&ci->home->attrs, val, ci->str);
89
90         return 1;
91 }
92
93 DEF_CMD(selection_claim)
94 {
95         struct window_data *wd = ci->home->data;
96
97         if (wd->sel_owner && wd->sel_owner != ci->focus) {
98                 call("Notify:selection:claimed", wd->sel_owner);
99                 //pane_drop_notifiers(ci->home, "Notify:Close", wd->sel_owner);
100         }
101         wd->sel_owner = ci->focus;
102         if (ci->num == 1)
103                 wd->sel_owner_fallback = ci->focus;
104         wd->sel_committed = 0;
105         pane_add_notify(ci->home, ci->focus, "Notify:Close");
106         return 1;
107 }
108
109 DEF_CMD(selection_commit)
110 {
111         struct window_data *wd = ci->home->data;
112
113         if (wd->sel_owner && !wd->sel_committed) {
114                 if (call("Notify:selection:commit", wd->sel_owner) != 2)
115                         wd->sel_committed = 1;
116         }
117         return 1;
118 }
119
120 DEF_CMD(selection_discard)
121 {
122         struct window_data *wd = ci->home->data;
123         struct pane *op, *fp;
124
125         if (!wd->sel_owner)
126                 return Efalse;
127         if (wd->sel_owner_fallback == ci->focus)
128                 wd->sel_owner_fallback = NULL;
129         /* Don't require exactly same pane for sel_owner,
130          * but ensure they have the same focus.
131          */
132         op = pane_leaf(wd->sel_owner);
133         fp = pane_leaf(ci->focus);
134         if (fp != op)
135                 return Efalse;
136
137         wd->sel_owner = wd->sel_owner_fallback;
138         wd->sel_committed = 0;
139         return 1;
140 }
141
142 DEF_CMD(close_notify)
143 {
144         struct window_data *wd = ci->home->data;
145
146         if (wd->sel_owner_fallback == ci->focus)
147                 wd->sel_owner_fallback = NULL;
148
149         if (wd->sel_owner == ci->focus)
150                 wd->sel_owner = wd->sel_owner_fallback;
151         return 1;
152 }
153
154 static struct map *window_map;
155 DEF_LOOKUP_CMD(window_handle, window_map);
156
157 DEF_CMD(window_attach)
158 {
159         struct pane *p;
160
161         p = pane_register(ci->focus, 0, &window_handle.c);
162         if (!p)
163                 return Efail;
164         comm_call(ci->comm2, "cb", p);
165         return 1;
166 }
167
168 DEF_CMD(window_close)
169 {
170         pane_close(ci->home);
171         return 1;
172 }
173
174 void window_setup(struct pane *ed safe)
175 {
176         window_map = key_alloc();
177
178         key_add_prefix(window_map, "window:request:", &request_notify);
179         key_add_prefix(window_map, "window:notify:", &send_notify);
180
181         key_add(window_map, "Display:close", &window_close);
182
183         key_add_prefix(window_map, "window:set:", &window_set);
184
185         key_add(window_map, "selection:claim", &selection_claim);
186         key_add(window_map, "selection:commit", &selection_commit);
187         key_add(window_map, "selection:discard", &selection_discard);
188         key_add(window_map, "Notify:Close", &close_notify);
189
190         call_comm("global-set-command", ed, &window_attach, 0, NULL,
191                   "attach-window-core");
192 }