added SDL backend for easier testing
[winnie] / src / fbdev / mouse.cc
1 #ifdef WINNIE_FBDEV
2 #include <errno.h>
3 #include <limits.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7
8 #include <fcntl.h>
9 #include <sys/ioctl.h>
10 #include <termios.h>
11 #include <unistd.h>
12
13 #include "geom.h"
14 #include "gfx.h"
15 #include "mouse.h"
16 #include "window.h"
17 #include "wm.h"
18
19 #define BN_LEFT         1
20 #define BN_RIGHT        2
21 #define BN_MIDDLE       4
22
23 static int read_mouse();
24
25 static int dev_fd = -1; // file descriptor for /dev/psaux
26 static Rect bounds;
27 static int pointer_x, pointer_y;
28 static int bnstate;
29
30 bool init_mouse()
31 {
32         if((dev_fd = open("/dev/psaux", O_RDONLY | O_NONBLOCK)) == -1) {
33                 fprintf(stderr, "Cannot open /dev/psaux : %s\n", strerror(errno));
34                 return false;
35         }
36
37         set_mouse_bounds(get_screen_size());
38         return true;
39 }
40
41 void destroy_mouse()
42 {
43         if(dev_fd != -1) {
44                 close(dev_fd);
45                 dev_fd = -1;
46         }
47 }
48
49 void set_mouse_bounds(const Rect &rect)
50 {
51         bounds = rect;
52 }
53
54 int get_mouse_fd()
55 {
56         return dev_fd;
57 }
58
59 void process_mouse_event()
60 {
61         /* TODO:
62          * - read all pending events from mouse fd (use O_NONBLOCK so that
63          *   read will return -1 when there are no more events instead of blocking).
64          */
65
66         int prev_state = bnstate;
67         int prev_x = pointer_x;
68         int prev_y = pointer_y;
69
70         if(read_mouse() == -1) {
71                 return;
72         }
73
74         Window *top = wm->get_window_at_pos(pointer_x, pointer_y);
75         if(top) {
76                 wm->set_focused_window(top);
77         }
78         else {
79                 wm->set_focused_window(0);
80         }
81
82          /* - send each pointer move and button press/release to the topmost window
83          *   with the pointer on it.
84          */
85
86         int dx = pointer_x - prev_x;
87         int dy = pointer_y - prev_y;
88
89         if((dx || dy) && top) {
90                 MouseMotionFuncType motion_callback = top->get_mouse_motion_callback();
91                 if(motion_callback) {
92                         Rect rect = top->get_absolute_rect();
93                         motion_callback(top, pointer_x - rect.x, pointer_y - rect.y);
94                 }
95         }
96
97         MouseButtonFuncType button_callback;
98         if((bnstate != prev_state) && top && (button_callback = top->get_mouse_button_callback())) {
99                 int num_bits = sizeof bnstate * CHAR_BIT;
100                 for(int i=0; i<num_bits; i++) {
101                         int s = (bnstate >> i) & 1;
102                         int prev_s = (prev_state >> i) & 1;
103                         if(s != prev_s) {
104                                 button_callback(top, i, s);
105                         }
106                 }
107         }
108 }
109
110 void get_pointer_pos(int *x, int *y)
111 {
112         *x = pointer_x;
113         *y = pointer_y;
114 }
115
116 int get_button_state()
117 {
118         return bnstate;
119 }
120
121 int get_button(int bn)
122 {
123         if(bn < 0 || bn >= 3) {
124                 return 0;
125         }
126         return (bnstate & (1 << bn)) != 0;
127 }
128
129 static int read_mouse()
130 {
131         int rd;
132         signed char state[3] = {0, 0, 0};
133
134         if((rd = read(dev_fd, state, 3)) == -1) {
135                 fprintf(stderr, "Unable to get mouse state : %s\n", strerror(errno));
136                 return -1;
137         }
138
139         bnstate = state[0] & 7;
140         pointer_x += state[1];
141         pointer_y -= state[2];
142
143         if(pointer_x < bounds.x) {
144                 pointer_x = bounds.x;
145         }
146
147         if(pointer_y < bounds.y) {
148                 pointer_y = bounds.y;
149         }
150
151         if(pointer_x > bounds.x + bounds.width - 1) {
152                 pointer_x = bounds.x + bounds.width - 1;
153         }
154
155         if(pointer_y > bounds.y + bounds.height - 1) {
156                 pointer_y = bounds.y + bounds.height - 1;
157         }
158
159         return 0;
160 }
161 #endif // WINNIE_FBDEV