-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlibvenowm.c
248 lines (200 loc) · 6.36 KB
/
libvenowm.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include "libvenowm.h"
#include "protocol/venowm-shell-client-protocol.h"
struct venowm {
bool connected;
struct wl_display *display;
bool free_display_in_cleanup;
struct wl_registry *registry;
uint32_t global_uid;
bool failed;
char errmsg[1024];
// the wayland interface to the venowm_control protocol
struct venowm_control *venowm_control;
};
// like snprintf(v->errmsg, sizeof(v->errmsg), fmt, ...), but safe and concise
static inline void errmsg(struct venowm *v, const char *fmt, ...){
// don't overwrite the first error
if(v->failed) return;
va_list ap;
va_start(ap, fmt);
vsnprintf(v->errmsg, sizeof(v->errmsg), fmt, ap);
va_end(ap);
// forcibly null terminate
v->errmsg[sizeof(v->errmsg) - 1] = '\0';
// indicate we have an error
v->failed = true;
}
static void registry_handle_global(void *data, struct wl_registry *registry,
uint32_t uid, const char *interface, uint32_t version){
struct venowm *v = data;
if(strcmp(interface, "venowm_control") == 0){
v->venowm_control = wl_registry_bind(registry, uid, &venowm_control_interface, 1);
v->global_uid = uid;
v->connected = true;
}
}
static void registry_handle_global_remove(void *data,
struct wl_registry *registry, uint32_t uid){
struct venowm *v = data;
if(v->global_uid == uid){
errmsg(v, "venowm global disappeared from registry");
}
}
static const struct wl_registry_listener registry_listener = {
registry_handle_global,
registry_handle_global_remove,
};
struct venowm *venowm_create(){
struct venowm *v = malloc(sizeof(struct venowm));
if(v == NULL) return v;
*v = (struct venowm){0};
return v;
}
void venowm_destroy(struct venowm *v){
if(v == NULL) return;
if(v->venowm_control != NULL) venowm_control_destroy(v->venowm_control);
if(v->registry != NULL) wl_registry_destroy(v->registry);
if(v->display != NULL && v->free_display_in_cleanup){
wl_display_disconnect(v->display);
}
free(v);
}
const char *venowm_errmsg(struct venowm *v){
return v->errmsg;
}
int venowm_connect(struct venowm *v, struct wl_display *wl_display){
if(v->failed) return -1;
if(v->connected){
errmsg(v, "already connected!");
return -1;
}
// do we have to find the display ourselves?
if(wl_display == NULL){
// we access the display ourselves
wl_display = wl_display_connect(NULL);
if(wl_display == NULL){
errmsg(v, "failed to connect to display");
return -1;
}
v->free_display_in_cleanup = true;
}
v->display = wl_display;
// get the registry
v->registry = wl_display_get_registry(v->display);
if(v->registry == NULL){
errmsg(v, "failed to get registry from display");
return -1;
}
// add our listener to the registry
int ret = wl_registry_add_listener(v->registry, ®istry_listener, v);
if(ret < 0){
errmsg(v, "failed to get add listener to registry");
return -1;
}
// sync with the server, getting all the initial globals
ret = wl_display_roundtrip(v->display);
if(ret < 0){
errmsg(v, "failed to sync with display server");
return -1;
}
// confirm that we found the venowm_control global
if(!v->connected){
errmsg(v, "unable to find venowm in wayland registry");
return -1;
}
return 0;
}
int venowm_flush(struct venowm *v){
if(v->failed) return -1;
if(!v->connected){
errmsg(v, "not connected yet!");
return -1;
}
int ret = wl_display_roundtrip(v->display);
if(ret < 0){
errmsg(v, "failed to sync with display server");
return -1;
}
return 0;
}
// a type for all of the simple venowm_control commands
typedef void (*venowm_cmd_t)(struct venowm_control*);
static int do_venowm_command(struct venowm *v, bool flush, venowm_cmd_t cmd){
if(v->failed) return -1;
if(!v->connected){
errmsg(v, "not connected yet!");
return -1;
}
cmd(v->venowm_control);
if(!flush) return 0;
return venowm_flush(v);
}
int venowm_focus_up(struct venowm *v, bool flush){
return do_venowm_command(v, flush, venowm_control_focus_up);
}
int venowm_focus_down(struct venowm *v, bool flush){
return do_venowm_command(v, flush, venowm_control_focus_down);
}
int venowm_focus_left(struct venowm *v, bool flush){
return do_venowm_command(v, flush, venowm_control_focus_left);
}
int venowm_focus_right(struct venowm *v, bool flush){
return do_venowm_command(v, flush, venowm_control_focus_right);
}
int venowm_launch(struct venowm *v, int argc, char **argv){
int retval = 0;
if(v->failed) return -1;
if(!v->connected){
errmsg(v, "not connected yet!");
return -1;
}
// count arg lengths (including \0)
size_t argv_concat_len = 0;
for(int i = 0; i < argc; i++){
argv_concat_len += strlen(argv[i]) + 1;
}
struct wl_array argv_array;
struct wl_array argvlen_array;
wl_array_init(&argv_array);
wl_array_init(&argvlen_array);
// allocate the concatenated argv
char *argv_concat = wl_array_add(&argv_array,
sizeof(*argv_concat) * argv_concat_len);
if(!argv_concat){
errmsg(v, "failed to copy argv");
retval = -1;
goto cu_arrays;
}
// allocated the array of lengths
uint32_t *arglen = wl_array_add(&argvlen_array, sizeof(*arglen) * argc);
if(!arglen){
errmsg(v, "failed to copy argv");
retval = -1;
goto cu_arrays;
}
// pack both of the arrays
char *p = argv_concat;
for(int i = 0; i < argc; i++){
uint32_t terminated_strlen = strlen(argv[i]) + 1;
arglen[i] = terminated_strlen;
memcpy(p, argv[i], terminated_strlen);
p += terminated_strlen;
}
// send the packed arrays over the wire
venowm_control_launch(v->venowm_control, &argv_array, &argvlen_array);
// can't release resources until we know the message was sent
int ret = wl_display_roundtrip(v->display);
if(ret < 0){
errmsg(v, "failed to sync with display server");
retval = -1;
goto cu_arrays;
}
cu_arrays:
wl_array_release(&argv_array);
wl_array_release(&argvlen_array);
return retval;
}