aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/background-image.c2
-rw-r--r--common/cairo.c4
-rw-r--r--common/gesture.c350
-rw-r--r--common/meson.build1
-rw-r--r--common/pango.c30
-rw-r--r--common/util.c12
6 files changed, 381 insertions, 18 deletions
diff --git a/common/background-image.c b/common/background-image.c
index de42e8e9..994a0805 100644
--- a/common/background-image.c
+++ b/common/background-image.c
@@ -1,6 +1,6 @@
1#include <assert.h> 1#include <assert.h>
2#include "background-image.h" 2#include "background-image.h"
3#include "cairo.h" 3#include "cairo_util.h"
4#include "log.h" 4#include "log.h"
5#if HAVE_GDK_PIXBUF 5#if HAVE_GDK_PIXBUF
6#include <gdk-pixbuf/gdk-pixbuf.h> 6#include <gdk-pixbuf/gdk-pixbuf.h>
diff --git a/common/cairo.c b/common/cairo.c
index 403dcf49..7c59d48c 100644
--- a/common/cairo.c
+++ b/common/cairo.c
@@ -1,6 +1,6 @@
1#include <stdint.h> 1#include <stdint.h>
2#include <cairo/cairo.h> 2#include <cairo.h>
3#include "cairo.h" 3#include "cairo_util.h"
4 4
5void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { 5void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
6 cairo_set_source_rgba(cairo, 6 cairo_set_source_rgba(cairo,
diff --git a/common/gesture.c b/common/gesture.c
new file mode 100644
index 00000000..8c2efe99
--- /dev/null
+++ b/common/gesture.c
@@ -0,0 +1,350 @@
1#define _POSIX_C_SOURCE 200809L
2#include "gesture.h"
3
4#include <math.h>
5#include <stdarg.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include "list.h"
10#include "log.h"
11#include "stringop.h"
12
13const uint8_t GESTURE_FINGERS_ANY = 0;
14
15// Helper to easily allocate and format string
16static char *strformat(const char *format, ...) {
17 va_list args;
18 va_start(args, format);
19 int length = vsnprintf(NULL, 0, format, args) + 1;
20 va_end(args);
21
22 char *result = malloc(length);
23 if (result) {
24 va_start(args, format);
25 vsnprintf(result, length, format, args);
26 va_end(args);
27 }
28
29 return result;
30}
31
32char *gesture_parse(const char *input, struct gesture *output) {
33 // Clear output in case of failure
34 output->type = GESTURE_TYPE_NONE;
35 output->fingers = GESTURE_FINGERS_ANY;
36 output->directions = GESTURE_DIRECTION_NONE;
37
38 // Split input type, fingers and directions
39 list_t *split = split_string(input, ":");
40 if (split->length < 1 || split->length > 3) {
41 return strformat(
42 "expected <gesture>[:<fingers>][:direction], got %s",
43 input);
44 }
45
46 // Parse gesture type
47 if (strcmp(split->items[0], "hold") == 0) {
48 output->type = GESTURE_TYPE_HOLD;
49 } else if (strcmp(split->items[0], "pinch") == 0) {
50 output->type = GESTURE_TYPE_PINCH;
51 } else if (strcmp(split->items[0], "swipe") == 0) {
52 output->type = GESTURE_TYPE_SWIPE;
53 } else {
54 return strformat("expected hold|pinch|swipe, got %s",
55 split->items[0]);
56 }
57
58 // Parse optional arguments
59 if (split->length > 1) {
60 char *next = split->items[1];
61
62 // Try to parse as finger count (1-9)
63 if (strlen(next) == 1 && '1' <= next[0] && next[0] <= '9') {
64 output->fingers = atoi(next);
65
66 // Move to next if available
67 next = split->length == 3 ? split->items[2] : NULL;
68 } else if (split->length == 3) {
69 // Fail here if argument can only be finger count
70 return strformat("expected 1-9, got %s", next);
71 }
72
73 // If there is an argument left, try to parse as direction
74 if (next) {
75 list_t *directions = split_string(next, "+");
76
77 for (int i = 0; i < directions->length; ++i) {
78 const char *item = directions->items[i];
79 if (strcmp(item, "any") == 0) {
80 continue;
81 } else if (strcmp(item, "up") == 0) {
82 output->directions |= GESTURE_DIRECTION_UP;
83 } else if (strcmp(item, "down") == 0) {
84 output->directions |= GESTURE_DIRECTION_DOWN;
85 } else if (strcmp(item, "left") == 0) {
86 output->directions |= GESTURE_DIRECTION_LEFT;
87 } else if (strcmp(item, "right") == 0) {
88 output->directions |= GESTURE_DIRECTION_RIGHT;
89 } else if (strcmp(item, "inward") == 0) {
90 output->directions |= GESTURE_DIRECTION_INWARD;
91 } else if (strcmp(item, "outward") == 0) {
92 output->directions |= GESTURE_DIRECTION_OUTWARD;
93 } else if (strcmp(item, "clockwise") == 0) {
94 output->directions |= GESTURE_DIRECTION_CLOCKWISE;
95 } else if (strcmp(item, "counterclockwise") == 0) {
96 output->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE;
97 } else {
98 return strformat("expected direction, got %s", item);
99 }
100 }
101 list_free_items_and_destroy(directions);
102 }
103 } // if optional args
104
105 list_free_items_and_destroy(split);
106
107 return NULL;
108}
109
110const char *gesture_type_string(enum gesture_type type) {
111 switch (type) {
112 case GESTURE_TYPE_NONE:
113 return "none";
114 case GESTURE_TYPE_HOLD:
115 return "hold";
116 case GESTURE_TYPE_PINCH:
117 return "pinch";
118 case GESTURE_TYPE_SWIPE:
119 return "swipe";
120 }
121
122 return NULL;
123}
124
125const char *gesture_direction_string(enum gesture_direction direction) {
126 switch (direction) {
127 case GESTURE_DIRECTION_NONE:
128 return "none";
129 case GESTURE_DIRECTION_UP:
130 return "up";
131 case GESTURE_DIRECTION_DOWN:
132 return "down";
133 case GESTURE_DIRECTION_LEFT:
134 return "left";
135 case GESTURE_DIRECTION_RIGHT:
136 return "right";
137 case GESTURE_DIRECTION_INWARD:
138 return "inward";
139 case GESTURE_DIRECTION_OUTWARD:
140 return "outward";
141 case GESTURE_DIRECTION_CLOCKWISE:
142 return "clockwise";
143 case GESTURE_DIRECTION_COUNTERCLOCKWISE:
144 return "counterclockwise";
145 }
146
147 return NULL;
148}
149
150// Helper to turn combination of directions flags into string representation.
151static char *gesture_directions_to_string(uint32_t directions) {
152 char *result = NULL;
153
154 for (uint8_t bit = 0; bit < 32; bit++) {
155 uint32_t masked = directions & (1 << bit);
156 if (masked) {
157 const char *name = gesture_direction_string(masked);
158
159 if (!name) {
160 name = "unknown";
161 }
162
163 if (!result) {
164 result = strdup(name);
165 } else {
166 char *new = strformat("%s+%s", result, name);
167 free(result);
168 result = new;
169 }
170 }
171 }
172
173 if(!result) {
174 return strdup("any");
175 }
176
177 return result;
178}
179
180char *gesture_to_string(struct gesture *gesture) {
181 char *directions = gesture_directions_to_string(gesture->directions);
182 char *result = strformat("%s:%u:%s",
183 gesture_type_string(gesture->type),
184 gesture->fingers, directions);
185 free(directions);
186 return result;
187}
188
189bool gesture_check(struct gesture *target, enum gesture_type type, uint8_t fingers) {
190 // Check that gesture type matches
191 if (target->type != type) {
192 return false;
193 }
194
195 // Check that finger count matches
196 if (target->fingers != GESTURE_FINGERS_ANY && target->fingers != fingers) {
197 return false;
198 }
199
200 return true;
201}
202
203bool gesture_match(struct gesture *target, struct gesture *to_match, bool exact) {
204 // Check type and fingers
205 if (!gesture_check(target, to_match->type, to_match->fingers)) {
206 return false;
207 }
208
209 // Enforce exact matches ...
210 if (exact && target->directions != to_match->directions) {
211 return false;
212 }
213
214 // ... or ensure all target directions are matched
215 return (target->directions & to_match->directions) == target->directions;
216}
217
218bool gesture_equal(struct gesture *a, struct gesture *b) {
219 return a->type == b->type
220 && a->fingers == b->fingers
221 && a->directions == b->directions;
222}
223
224// Return count of set bits in directions bit field.
225static uint8_t gesture_directions_count(uint32_t directions) {
226 uint8_t count = 0;
227 for (; directions; directions >>= 1) {
228 count += directions & 1;
229 }
230 return count;
231}
232
233// Compare direction bit count of two direction.
234static int8_t gesture_directions_compare(uint32_t a, uint32_t b) {
235 return gesture_directions_count(a) - gesture_directions_count(b);
236}
237
238// Compare two direction based on their direction bit count
239int8_t gesture_compare(struct gesture *a, struct gesture *b) {
240 return gesture_directions_compare(a->directions, b->directions);
241}
242
243void gesture_tracker_begin(struct gesture_tracker *tracker,
244 enum gesture_type type, uint8_t fingers) {
245 tracker->type = type;
246 tracker->fingers = fingers;
247
248 tracker->dx = 0.0;
249 tracker->dy = 0.0;
250 tracker->scale = 1.0;
251 tracker->rotation = 0.0;
252
253 sway_log(SWAY_DEBUG, "begin tracking %s:%u:? gesture",
254 gesture_type_string(type), fingers);
255}
256
257bool gesture_tracker_check(struct gesture_tracker *tracker, enum gesture_type type) {
258 return tracker->type == type;
259}
260
261void gesture_tracker_update(struct gesture_tracker *tracker,
262 double dx, double dy, double scale, double rotation) {
263 if (tracker->type == GESTURE_TYPE_HOLD) {
264 sway_assert(false, "hold does not update.");
265 return;
266 }
267
268 tracker->dx += dx;
269 tracker->dy += dy;
270
271 if (tracker->type == GESTURE_TYPE_PINCH) {
272 tracker->scale = scale;
273 tracker->rotation += rotation;
274 }
275
276 sway_log(SWAY_DEBUG, "update tracking %s:%u:? gesture: %f %f %f %f",
277 gesture_type_string(tracker->type),
278 tracker->fingers,
279 tracker->dx, tracker->dy,
280 tracker->scale, tracker->rotation);
281}
282
283void gesture_tracker_cancel(struct gesture_tracker *tracker) {
284 sway_log(SWAY_DEBUG, "cancel tracking %s:%u:? gesture",
285 gesture_type_string(tracker->type), tracker->fingers);
286
287 tracker->type = GESTURE_TYPE_NONE;
288}
289
290struct gesture *gesture_tracker_end(struct gesture_tracker *tracker) {
291 struct gesture *result = calloc(1, sizeof(struct gesture));
292
293 // Ignore gesture under some threshold
294 // TODO: Make configurable
295 const double min_rotation = 5;
296 const double min_scale_delta = 0.1;
297
298 // Determine direction
299 switch(tracker->type) {
300 // Gestures with scale and rotation
301 case GESTURE_TYPE_PINCH:
302 if (tracker->rotation > min_rotation) {
303 result->directions |= GESTURE_DIRECTION_CLOCKWISE;
304 }
305 if (tracker->rotation < -min_rotation) {
306 result->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE;
307 }
308
309 if (tracker->scale > (1.0 + min_scale_delta)) {
310 result->directions |= GESTURE_DIRECTION_OUTWARD;
311 }
312 if (tracker->scale < (1.0 - min_scale_delta)) {
313 result->directions |= GESTURE_DIRECTION_INWARD;
314 }
315 __attribute__ ((fallthrough));
316 // Gestures with dx and dy
317 case GESTURE_TYPE_SWIPE:
318 if (fabs(tracker->dx) > fabs(tracker->dy)) {
319 if (tracker->dx > 0) {
320 result->directions |= GESTURE_DIRECTION_RIGHT;
321 } else {
322 result->directions |= GESTURE_DIRECTION_LEFT;
323 }
324 } else {
325 if (tracker->dy > 0) {
326 result->directions |= GESTURE_DIRECTION_DOWN;
327 } else {
328 result->directions |= GESTURE_DIRECTION_UP;
329 }
330 }
331 // Gesture without any direction
332 case GESTURE_TYPE_HOLD:
333 break;
334 // Not tracking any gesture
335 case GESTURE_TYPE_NONE:
336 sway_assert(false, "Not tracking any gesture.");
337 return result;
338 }
339
340 result->type = tracker->type;
341 result->fingers = tracker->fingers;
342
343 char *description = gesture_to_string(result);
344 sway_log(SWAY_DEBUG, "end tracking gesture: %s", description);
345 free(description);
346
347 tracker->type = GESTURE_TYPE_NONE;
348
349 return result;
350}
diff --git a/common/meson.build b/common/meson.build
index c653dd72..3756075a 100644
--- a/common/meson.build
+++ b/common/meson.build
@@ -3,6 +3,7 @@ lib_sway_common = static_library(
3 files( 3 files(
4 'background-image.c', 4 'background-image.c',
5 'cairo.c', 5 'cairo.c',
6 'gesture.c',
6 'ipc-client.c', 7 'ipc-client.c',
7 'log.c', 8 'log.c',
8 'loop.c', 9 'loop.c',
diff --git a/common/pango.c b/common/pango.c
index fc3d0688..e04bf80f 100644
--- a/common/pango.c
+++ b/common/pango.c
@@ -1,4 +1,4 @@
1#include <cairo/cairo.h> 1#include <cairo.h>
2#include <pango/pangocairo.h> 2#include <pango/pangocairo.h>
3#include <stdarg.h> 3#include <stdarg.h>
4#include <stdbool.h> 4#include <stdbool.h>
@@ -6,7 +6,7 @@
6#include <stdio.h> 6#include <stdio.h>
7#include <stdlib.h> 7#include <stdlib.h>
8#include <string.h> 8#include <string.h>
9#include "cairo.h" 9#include "cairo_util.h"
10#include "log.h" 10#include "log.h"
11#include "stringop.h" 11#include "stringop.h"
12 12
@@ -50,7 +50,7 @@ size_t escape_markup_text(const char *src, char *dest) {
50 return length; 50 return length;
51} 51}
52 52
53PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, 53PangoLayout *get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc,
54 const char *text, double scale, bool markup) { 54 const char *text, double scale, bool markup) {
55 PangoLayout *layout = pango_cairo_create_layout(cairo); 55 PangoLayout *layout = pango_cairo_create_layout(cairo);
56 PangoAttrList *attrs; 56 PangoAttrList *attrs;
@@ -73,16 +73,14 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
73 } 73 }
74 74
75 pango_attr_list_insert(attrs, pango_attr_scale_new(scale)); 75 pango_attr_list_insert(attrs, pango_attr_scale_new(scale));
76 PangoFontDescription *desc = pango_font_description_from_string(font);
77 pango_layout_set_font_description(layout, desc); 76 pango_layout_set_font_description(layout, desc);
78 pango_layout_set_single_paragraph_mode(layout, 1); 77 pango_layout_set_single_paragraph_mode(layout, 1);
79 pango_layout_set_attributes(layout, attrs); 78 pango_layout_set_attributes(layout, attrs);
80 pango_attr_list_unref(attrs); 79 pango_attr_list_unref(attrs);
81 pango_font_description_free(desc);
82 return layout; 80 return layout;
83} 81}
84 82
85void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, 83void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height,
86 int *baseline, double scale, bool markup, const char *fmt, ...) { 84 int *baseline, double scale, bool markup, const char *fmt, ...) {
87 va_list args; 85 va_list args;
88 va_start(args, fmt); 86 va_start(args, fmt);
@@ -99,7 +97,7 @@ void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
99 vsnprintf(buf, length, fmt, args); 97 vsnprintf(buf, length, fmt, args);
100 va_end(args); 98 va_end(args);
101 99
102 PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); 100 PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup);
103 pango_cairo_update_layout(cairo, layout); 101 pango_cairo_update_layout(cairo, layout);
104 pango_layout_get_pixel_size(layout, width, height); 102 pango_layout_get_pixel_size(layout, width, height);
105 if (baseline) { 103 if (baseline) {
@@ -109,7 +107,21 @@ void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
109 free(buf); 107 free(buf);
110} 108}
111 109
112void pango_printf(cairo_t *cairo, const char *font, 110void get_text_metrics(const PangoFontDescription *description, int *height, int *baseline) {
111 cairo_t *cairo = cairo_create(NULL);
112 PangoContext *pango = pango_cairo_create_context(cairo);
113 // When passing NULL as a language, pango uses the current locale.
114 PangoFontMetrics *metrics = pango_context_get_metrics(pango, description, NULL);
115
116 *baseline = pango_font_metrics_get_ascent(metrics) / PANGO_SCALE;
117 *height = *baseline + pango_font_metrics_get_descent(metrics) / PANGO_SCALE;
118
119 pango_font_metrics_unref(metrics);
120 g_object_unref(pango);
121 cairo_destroy(cairo);
122}
123
124void render_text(cairo_t *cairo, const PangoFontDescription *desc,
113 double scale, bool markup, const char *fmt, ...) { 125 double scale, bool markup, const char *fmt, ...) {
114 va_list args; 126 va_list args;
115 va_start(args, fmt); 127 va_start(args, fmt);
@@ -126,7 +138,7 @@ void pango_printf(cairo_t *cairo, const char *font,
126 vsnprintf(buf, length, fmt, args); 138 vsnprintf(buf, length, fmt, args);
127 va_end(args); 139 va_end(args);
128 140
129 PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); 141 PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup);
130 cairo_font_options_t *fo = cairo_font_options_create(); 142 cairo_font_options_t *fo = cairo_font_options_create();
131 cairo_get_font_options(cairo, fo); 143 cairo_get_font_options(cairo, fo);
132 pango_cairo_context_set_font_options(pango_layout_get_context(layout), fo); 144 pango_cairo_context_set_font_options(pango_layout_get_context(layout), fo);
diff --git a/common/util.c b/common/util.c
index 5ea94f48..5d4c0673 100644
--- a/common/util.c
+++ b/common/util.c
@@ -10,12 +10,6 @@
10#include "log.h" 10#include "log.h"
11#include "util.h" 11#include "util.h"
12 12
13uint32_t get_current_time_msec(void) {
14 struct timespec now;
15 clock_gettime(CLOCK_MONOTONIC, &now);
16 return now.tv_sec * 1000 + now.tv_nsec / 1000000;
17}
18
19int wrap(int i, int max) { 13int wrap(int i, int max) {
20 return ((i % max) + max) % max; 14 return ((i % max) + max) % max;
21} 15}
@@ -86,6 +80,12 @@ enum movement_unit parse_movement_unit(const char *unit) {
86 80
87int parse_movement_amount(int argc, char **argv, 81int parse_movement_amount(int argc, char **argv,
88 struct movement_amount *amount) { 82 struct movement_amount *amount) {
83 if (!sway_assert(argc > 0, "Expected args in parse_movement_amount")) {
84 amount->amount = 0;
85 amount->unit = MOVEMENT_UNIT_INVALID;
86 return 0;
87 }
88
89 char *err; 89 char *err;
90 amount->amount = (int)strtol(argv[0], &err, 10); 90 amount->amount = (int)strtol(argv[0], &err, 10);
91 if (*err) { 91 if (*err) {