/
event.c
169 lines (160 loc) · 6.2 KB
/
event.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
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "event.h"
#define BUTTON_COUNT 5
#define MOTION_THRESHOLD 15
struct button_state {
enum { init, pressed, wait, dragging } state;
int clickcount;
Time time;
int x, y;
};
struct event_state {
struct button_state button[BUTTON_COUNT];
};
static bool
far_enough(int x1, int y1, int x2, int y2)
{
int x = x1 - x2;
int y = y1 - y2;
return x * x + y * y > MOTION_THRESHOLD * MOTION_THRESHOLD;
}
struct event_state *
create_event_state(void)
{
struct event_state *e = malloc(sizeof(struct event_state));
for (int i = 0; i < BUTTON_COUNT; i++) {
e->button[i].state = init;
}
return e;
}
/* returns true if the X event was processed and the result is stored in the_event */
bool
process_event(XEvent *ev, struct the_event **event, struct event_state *es)
{
*event = NULL;
struct button_state *bs;
struct the_event *e;
switch (ev->type) {
case KeyPress: ;
KeySym keysym = XLookupKeysym(&ev->xkey, 0);
e = malloc(sizeof(*e));
e->next = *event;
*event = e;
e->clickcount = 1;
e->button = keysym;
e->x = ev->xkey.x_root;
e->y = ev->xkey.y_root;
e->type = key_press;
e->time = ev->xkey.time;
return true;
break;
case ButtonPress:
if (ev->xbutton.button > BUTTON_COUNT)
return false;
bs = &es->button[ev->xbutton.button - 1];
switch (bs->state) {
case init:
bs->state = pressed;
bs->clickcount = 0;
bs->time = ev->xbutton.time;
bs->x = ev->xbutton.x_root;
bs->y = ev->xbutton.y_root;
return true;
default:
fprintf(stderr, "Weird state: ButtonPress\n");
abort();
}
case ButtonRelease:
if (ev->xbutton.button > BUTTON_COUNT)
return false;
bs = &es->button[ev->xbutton.button - 1];
switch (bs->state) {
case init:
return true; // discard spurious button release
case pressed:
if (!far_enough(ev->xbutton.x_root, ev->xbutton.y_root, bs->x, bs->y)) {
struct the_event *e = malloc(sizeof(struct the_event));
e->next = *event;
*event = e;
e->clickcount = 1;
e->button = ev->xbutton.button;
e->x = ev->xbutton.x_root;
e->y = ev->xbutton.y_root;
e->type = button_press;
e->time = ev->xbutton.time;
}
bs->state = init;
return true;
case dragging:
e = malloc(sizeof(struct the_event));
e->next = *event;
*event = e;
e->clickcount = 1;
e->button = ev->xbutton.button;
e->start_x = bs->x;
e->start_y = bs->y;
e->x = ev->xbutton.x_root;
e->y = ev->xbutton.y_root;
e->type = drag_end;
e->time = ev->xbutton.time;
bs->state = init;
return true;
default:
fprintf(stderr, "Weird state: ButtonRelease\n");
abort();
}
case MotionNotify: {
int drag = -1;
int press = -1;
for (int i = 0; i < BUTTON_COUNT; i++) {
if (es->button[i].state == dragging)
drag = i;
if (es->button[i].state == pressed)
press = i;
}
if (drag == -1 && press == -1)
return false;
if (drag == -1) {
bs = &es->button[press];
assert(bs->state == pressed);
if (far_enough(bs->x, bs->y, ev->xmotion.x_root, ev->xmotion.y_root)) {
bs->state = dragging;
// the drag_start
e = malloc(sizeof(struct the_event));
e->next = *event;
*event = e;
e->button = press + 1;
e->start_x = bs->x;
e->start_y = bs->y;
e->x = ev->xmotion.x_root;
e->y = ev->xmotion.y_root;
e->type = drag_start;
e->time = ev->xmotion.time;
}
return true;
} else {
bs = &es->button[drag];
assert(bs->state == dragging);
// the drag_update
e = malloc(sizeof(struct the_event));
e->next = *event;
*event = e;
e->button = drag + 1;
e->start_x = bs->x;
e->start_y = bs->y;
e->x = ev->xmotion.x_root;
e->y = ev->xmotion.y_root;
e->time = ev->xmotion.time;
e->type = drag_update;
return true;
}
}
case MappingNotify:
XRefreshKeyboardMapping(&(ev->xmapping));
return true;
}
return false;
}