1 /* wl-clipboard
2  *
3  * Copyright © 2019 Sergey Bugaev <bugaevc@gmail.com>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "types/registry.h"
20 #include "types/seat.h"
21 #include "types/shell.h"
22 #include "types/device-manager.h"
23 #include "includes/shell-protocols.h"
24 #include "includes/selection-protocols.h"
25 #include "util/misc.h"
26 
27 #include <string.h>
28 #include <stdlib.h>
29 
30 #define BIND(interface_name, known_version) \
31 if (strcmp(interface, #interface_name) == 0) { \
32     self->interface_name = wl_registry_bind( \
33         wl_registry, \
34         name, \
35         &interface_name ## _interface, \
36         known_version \
37     ); \
38 }
39 
wl_registry_global_handler(void * data,struct wl_registry * wl_registry,uint32_t name,const char * interface,uint32_t version)40 static void wl_registry_global_handler(
41     void *data,
42     struct wl_registry *wl_registry,
43     uint32_t name,
44     const char *interface,
45     uint32_t version
46 ) {
47     struct registry *self = (struct registry *) data;
48 
49     BIND(wl_compositor, 2)
50     BIND(wl_shm, 1)
51 
52     /* Shells */
53 
54     BIND(wl_shell, 1)
55 
56 #ifdef HAVE_XDG_SHELL
57     BIND(xdg_wm_base, 1)
58 #endif
59 
60     /* Device managers */
61 
62     BIND(wl_data_device_manager, 1)
63 
64 #ifdef HAVE_GTK_PRIMARY_SELECTION
65     BIND(gtk_primary_selection_device_manager, 1)
66 #endif
67 
68 #ifdef HAVE_WP_PRIMARY_SELECTION
69     BIND(zwp_primary_selection_device_manager_v1, 1)
70 #endif
71 
72 #ifdef HAVE_WLR_DATA_CONTROL
73     BIND(zwlr_data_control_manager_v1, version > 2 ? 2 : version)
74 #endif
75 
76     if (strcmp(interface, "wl_seat") == 0) {
77         struct seat *seat = calloc(1, sizeof(struct seat));
78         seat->proxy = wl_registry_bind(
79             wl_registry,
80             name,
81             &wl_seat_interface,
82             2
83         );
84         seat_init(seat);
85         struct seat **ptr = wl_array_add(&self->seats, sizeof(struct seat *));
86         *ptr = seat;
87     }
88 }
89 
wl_registry_global_remove_handler(void * data,struct wl_registry * wl_registry,uint32_t name)90 static void wl_registry_global_remove_handler(
91     void *data,
92     struct wl_registry *wl_registry,
93     uint32_t name
94 ) {}
95 
96 static const struct wl_registry_listener wl_registry_listener = {
97     .global = wl_registry_global_handler,
98     .global_remove = wl_registry_global_remove_handler
99 };
100 
registry_init(struct registry * self)101 void registry_init(struct registry *self) {
102     self->proxy = wl_display_get_registry(self->wl_display);
103     wl_registry_add_listener(self->proxy, &wl_registry_listener, self);
104 }
105 
registry_find_shell(struct registry * self)106 struct shell *registry_find_shell(struct registry *self) {
107     struct shell *shell = calloc(1, sizeof(struct shell));
108 
109     if (self->wl_shell != NULL) {
110         shell->proxy = (struct wl_proxy *) self->wl_shell;
111         shell_init_wl_shell(shell);
112         return shell;
113     }
114 
115 #ifdef HAVE_XDG_SHELL
116     if (self->xdg_wm_base != NULL) {
117         shell->proxy = (struct wl_proxy *) self->xdg_wm_base;
118         shell_init_xdg_shell(shell);
119         return shell;
120     }
121 #endif
122 
123     free(shell);
124     return NULL;
125 }
126 
127 #define TRY(type) \
128 if (self->type != NULL) { \
129     device_manager->proxy = (struct wl_proxy *) self->type; \
130     device_manager_init_ ## type(device_manager); \
131     return device_manager; \
132 }
133 
registry_find_device_manager(struct registry * self,int primary)134 struct device_manager *registry_find_device_manager(
135     struct registry *self,
136     int primary
137 ) {
138     struct device_manager *device_manager
139         = calloc(1, sizeof(struct device_manager));
140     device_manager->wl_display = self->wl_display;
141 
142     /* For regular selection, we just look at the two supported
143      * protocols. We prefer wlr-data-control, as it doesn't require
144      * us to use the popup surface hack.
145      */
146 
147     if (!primary) {
148 #ifdef HAVE_WLR_DATA_CONTROL
149         TRY(zwlr_data_control_manager_v1)
150 #endif
151         TRY(wl_data_device_manager)
152 
153         free(device_manager);
154         return NULL;
155     }
156 
157     /* For primary selection, it's a bit more complicated. We also
158      * prefer wlr-data-control, but we don't know in advance whether
159      * the compositor supports primary selection, as unlike with
160      * other protocols here, the mere presence of wlr-data-control
161      * does not imply primary selection support. However, we assume
162      * that if a compositor supports primary selection at all, then
163      * if it supports wlr-data-control v2 it also supports primary
164      * selection over wlr-data-control; which is only reasonable.
165      */
166 
167 #ifdef HAVE_WLR_DATA_CONTROL
168     if (self->zwlr_data_control_manager_v1 != NULL) {
169         struct wl_proxy *proxy
170             = (struct wl_proxy *) self->zwlr_data_control_manager_v1;
171         if (wl_proxy_get_version(proxy) >= 2) {
172             device_manager->proxy = proxy;
173             device_manager_init_zwlr_data_control_manager_v1(device_manager);
174             return device_manager;
175         }
176     }
177 #endif
178 
179 #ifdef HAVE_WP_PRIMARY_SELECTION
180     TRY(zwp_primary_selection_device_manager_v1)
181 #endif
182 
183 #ifdef HAVE_GTK_PRIMARY_SELECTION
184     TRY(gtk_primary_selection_device_manager)
185 #endif
186 
187     free(device_manager);
188     return NULL;
189 }
190 
registry_find_seat(struct registry * self,const char * name)191 struct seat *registry_find_seat(
192     struct registry *self,
193     const char *name
194 ) {
195     /* Ensure we get all the seat info */
196     wl_display_roundtrip(self->wl_display);
197 
198     struct seat **ptr;
199     wl_array_for_each(ptr, &self->seats) {
200         struct seat *seat = *ptr;
201         if (name == NULL || strcmp(seat->name, name) == 0) {
202             return seat;
203         }
204     }
205     return NULL;
206 }
207