1 /**
2  ** w32inp.c ---- DOS (TCC/BCC/DJGPP: "conio.h") style keyboard utilities
3  **
4  ** Author:     Gernot Graeff
5  ** E-mail:     gernot.graeff@t-online.de
6  ** Date:       02-11-99
7  **
8  ** This file is part of the GRX graphics library.
9  **
10  ** The GRX graphics library is free software; you can redistribute it
11  ** and/or modify it under some conditions; see the "copying.grx" file
12  ** for details.
13  **
14  ** This library is distributed in the hope that it will be useful,
15  ** but WITHOUT ANY WARRANTY; without even the implied warranty of
16  ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17  **
18  ** Contributions by M.Alvarez (malfer@teleline.es) 18/11/2001
19  **   - Better keys handling using translation tables (w32input.h).
20  **
21  ** Contributions by M.Alvarez (malfer@teleline.es) 02/02/2002
22  **   - The w32 imput queue implemented as a circular queue.
23  **   - All the input related code moved here from vd_win32.c
24  **
25  ** Contribution by M. Lombardi 05/08/2007
26  ** Do not treat WM_PAINT messages here. They are delt with in vd_win32.c.
27  ** This produced saturation of GRX event queue	and gobbling of
28  ** keyboard/mouse events there (compare behavior of test/mousetst)
29  **
30  ** Contribution by Richard Sanders (richard@dogcreek.ca) 02/04/2009
31  ** Synchronisation of windows and grx mouse cursors
32  **/
33 
34 #include "libwin32.h"
35 #include "libgrx.h"
36 #include "grxkeys.h"
37 #include "input.h"
38 #include "arith.h"
39 #include "memcopy.h"
40 #include "w32input.h"
41 
42 int _nkeysw32pool = 0;
43 int _keysw32pool[_MAXKEYSW32POOL];
44 
45 static int kbd_enabled = TRUE;
46 static int kbd_lastmod = 0;
47 static int kbd_hitcount = 0;
48 static int mou_enabled = TRUE;
49 static int mou_buttons = 0;
50 static long evt_lasttime;
51 
_GrIsKbdEnabled(void)52 int _GrIsKbdEnabled(void)
53 {
54     return kbd_enabled;
55 }
56 
_GrKeyPressed(void)57 int _GrKeyPressed(void)
58 {
59     _GrUpdateInputs();
60     if (kbd_enabled)
61         return (kbd_hitcount > 0);
62     else
63         return (_nkeysw32pool > 0);
64 }
65 
_GrKeyStat(void)66 int _GrKeyStat(void)
67 {
68     return kbd_lastmod;
69 }
70 
uninit(void)71 static void uninit(void)
72 {
73     if (MOUINFO->msstatus > 1) {
74 	MOUINFO->msstatus = 1;
75     }
76 }
77 
GrMouseDetect(void)78 int GrMouseDetect(void)
79 {
80     return GetSystemMetrics(SM_MOUSEPRESENT);
81 }
82 
init_w32queue(int queue_size)83 static void init_w32queue(int queue_size)
84 {
85     EnterCriticalSection(&_csEventQueue);
86     if (_W32EventQueueSize != queue_size) {
87 	if (_W32EventQueue != NULL)
88 	    free(_W32EventQueue);
89         _W32EventQueue = (W32Event *)malloc(sizeof(W32Event) * queue_size);
90 	_W32EventQueueSize = _W32EventQueue ? queue_size : 0;
91     }
92     _W32EventQueueRead = 0;
93     _W32EventQueueWrite = 0;
94     _W32EventQueueLength = 0;
95     LeaveCriticalSection(&_csEventQueue);
96 }
97 
GrMouseInitN(int queue_size)98 void GrMouseInitN(int queue_size)
99 {
100     uninit();
101     queue_size = umax(4, umin(256, queue_size));
102     init_queue(queue_size);
103     init_w32queue(queue_size);
104     kbd_hitcount = 0;
105     if (GrMouseDetect()) {
106 	GrMouseSetSpeed(1, 1);
107 	GrMouseSetAccel(100, 1);
108 	GrMouseSetLimits(0, 0, SCRN->gc_xmax, SCRN->gc_ymax);
109         GrMouseWarp((SCRN->gc_xmax >> 1), (SCRN->gc_ymax >> 1));
110 	_GrInitMouseCursor();
111 	MOUINFO->msstatus = 2;
112 	mou_buttons = 0;
113     }
114     GrMouseEventEnable(TRUE, TRUE);
115     real_time(evt_lasttime);
116     MOUINFO->uninit = uninit;
117 }
118 
GrMouseSetSpeed(int spmult,int spdiv)119 void GrMouseSetSpeed(int spmult, int spdiv)
120 {
121     MOUINFO->spmult = umin(16, umax(1, spmult));
122     MOUINFO->spdiv = umin(16, umax(1, spdiv));
123 }
124 
GrMouseSetAccel(int thresh,int accel)125 void GrMouseSetAccel(int thresh, int accel)
126 {
127     MOUINFO->thresh = umin(64, umax(1, thresh));
128     MOUINFO->accel = umin(16, umax(1, accel));
129 }
130 
GrMouseSetLimits(int x1,int y1,int x2,int y2)131 void GrMouseSetLimits(int x1, int y1, int x2, int y2)
132 {
133     isort(x1, x2);
134     isort(y1, y2);
135     MOUINFO->xmin = imax(0, imin(x1, SCRN->gc_xmax));
136     MOUINFO->ymin = imax(0, imin(y1, SCRN->gc_ymax));
137     MOUINFO->xmax = imax(0, imin(x2, SCRN->gc_xmax));
138     MOUINFO->ymax = imax(0, imin(y2, SCRN->gc_ymax));
139 }
140 
GrMouseWarp(int x,int y)141 void GrMouseWarp(int x, int y)
142 {
143     POINT point;
144 
145     MOUINFO->xpos = imax(MOUINFO->xmin, imin(MOUINFO->xmax, x));
146     MOUINFO->ypos = imax(MOUINFO->ymin, imin(MOUINFO->ymax, y));
147     GrMouseUpdateCursor();
148     point.x = MOUINFO->xpos;
149     point.y = MOUINFO->ypos;
150     ClientToScreen(hGRXWnd, &point);
151     SetCursorPos(point.x, point.y);
152 }
153 
GrMouseEventEnable(int enable_kb,int enable_ms)154 void GrMouseEventEnable(int enable_kb, int enable_ms)
155 {
156     kbd_enabled = enable_kb;
157     mou_enabled = enable_ms;
158 }
159 
GrMouseGetEventT(int flags,GrMouseEvent * ev,long tout)160 void GrMouseGetEventT(int flags, GrMouseEvent * ev, long tout)
161 {
162     int msdraw;
163 
164     if (MOUINFO->msstatus == 0) GrMouseInit();
165 
166     msdraw = !MOUINFO->displayed && !(flags & GR_M_NOPAINT);
167     if (msdraw) GrMouseDisplayCursor();
168 
169     if (tout <= 0L) tout = 1L;
170 
171     for (;;) {
172 	_GrUpdateInputs();
173 	GrMouseUpdateCursor();
174 	while (MOUINFO->qlength > 0) {
175 	    dequeue_event((*ev));
176             if (ev->flags & GR_M_KEYPRESS) kbd_hitcount--;
177 	    if (ev->flags & flags) {
178                 if (msdraw) GrMouseEraseCursor();
179 		return;
180 	    }
181 	}
182 	if ((flags & GR_M_POLL) ||
183 	    (tout == 0L) || (MOUINFO->moved && (flags & GR_M_MOTION))) {
184 	    fill_mouse_ev((*ev),
185 			  mou_buttons, mou_buttons,
186 			  GR_M_LEFT, GR_M_MIDDLE, GR_M_RIGHT, GR_M_P4, GR_M_P5, kbd_lastmod);
187 	    if (ev->flags)	/* something happend */
188 		real_dtime(ev->dtime, evt_lasttime);
189 	    else
190 		ev->dtime = -1;	/* special time if nothing happend */
191 	    MOUINFO->moved = FALSE;
192 	    if (msdraw) {
193 		GrMouseEraseCursor();
194 	    }
195 	    return;
196 	}
197 	if (tout > 0L) {
198 	    Sleep(10);
199             if ((tout -= 10) < 0L) tout = 0L;
200 	}
201     }
202 }
203 
StdKeyTranslate(int winkey,int fkbState)204 static GrKeyType StdKeyTranslate(int winkey, int fkbState)
205 {
206     keytrans *k;
207     int i;
208 
209     if (fkbState & GR_KB_ALT)
210 	k = altstdkeys;
211     else if (fkbState & GR_KB_CTRL)
212 	k = controlstdkeys;
213     else if (fkbState & GR_KB_SHIFT)
214 	k = shiftstdkeys;
215     else
216 	k = stdkeys;
217 
218     for (i = 0; i < NSTDKEYS; i++) {
219 	if (winkey == k[i].winkey)
220 	    return k[i].grkey;
221     }
222 
223     return 0;
224 }
225 
DequeueW32Event(GrMouseEvent * ev)226 static int DequeueW32Event(GrMouseEvent * ev)
227 {
228     W32Event evaux;
229     int key;
230     int buttons;
231 
232     if (_W32EventQueueLength < 1){
233 	Sleep(1); /* yield */
234 	return 0;
235     }
236 
237     EnterCriticalSection(&_csEventQueue);
238 //    if (!TryEnterCriticalSection(&_csEventQueue))
239 //        return 0;
240 
241     evaux = _W32EventQueue[_W32EventQueueRead];
242     if (++_W32EventQueueRead == _W32EventQueueSize)
243 	_W32EventQueueRead = 0;
244     _W32EventQueueLength--;
245     LeaveCriticalSection(&_csEventQueue);
246 
247     switch (evaux.uMsg) {
248 
249     case WM_CHAR:
250         fill_keybd_ev((*ev), evaux.wParam, evaux.kbstat);
251         kbd_lastmod = evaux.kbstat;
252         return 1;
253 
254     case WM_SYSCHAR:
255 	key = 0;
256 	if (evaux.wParam >= 'a' && evaux.wParam <= 'z')
257 	    key = altletters[evaux.wParam - 'a'];
258 	if (evaux.wParam >= 'A' && evaux.wParam <= 'Z')
259 	    key = altletters[evaux.wParam - 'A'];
260 	if (evaux.wParam >= '0' && evaux.wParam <= '9')
261 	    key = altnumbers[evaux.wParam - '0'];
262 	if (key == 0)
263 	    return -1;
264 	fill_keybd_ev((*ev), key, evaux.kbstat);
265 	kbd_lastmod = evaux.kbstat;
266 	return 1;
267 
268     case WM_KEYDOWN:
269     case WM_SYSKEYDOWN:
270 	key = StdKeyTranslate(evaux.wParam, evaux.kbstat);
271 	if (key == 0)
272 	    return -1;
273 	fill_keybd_ev((*ev), key, evaux.kbstat);
274 	kbd_lastmod = evaux.kbstat;
275 	return 1;
276 
277     case WM_COMMAND:
278         fill_cmd_ev((*ev), evaux.wParam, evaux.kbstat);
279         return 1;
280 
281     case WM_LBUTTONDOWN:
282 	buttons = GR_M_LEFT | mou_buttons;
283 	MOUINFO->xpos = LOWORD(evaux.lParam);
284 	MOUINFO->ypos = HIWORD(evaux.lParam);
285 	fill_mouse_ev((*ev), mou_buttons, buttons, GR_M_LEFT,
286 		      GR_M_MIDDLE, GR_M_RIGHT, GR_M_P4, GR_M_P5, evaux.kbstat);
287 	mou_buttons = buttons;
288 	MOUINFO->moved = FALSE;
289 	kbd_lastmod = evaux.kbstat;
290 	return 1;
291 
292     case WM_MBUTTONDOWN:
293 	buttons = GR_M_MIDDLE | mou_buttons;
294 	MOUINFO->xpos = LOWORD(evaux.lParam);
295 	MOUINFO->ypos = HIWORD(evaux.lParam);
296 	fill_mouse_ev((*ev), mou_buttons, buttons, GR_M_LEFT,
297 		      GR_M_MIDDLE, GR_M_RIGHT, GR_M_P4, GR_M_P5, evaux.kbstat);
298 	mou_buttons = buttons;
299 	MOUINFO->moved = FALSE;
300 	kbd_lastmod = evaux.kbstat;
301 	return 1;
302 
303     case WM_RBUTTONDOWN:
304 	buttons = GR_M_RIGHT | mou_buttons;
305 	MOUINFO->xpos = LOWORD(evaux.lParam);
306 	MOUINFO->ypos = HIWORD(evaux.lParam);
307 	fill_mouse_ev((*ev), mou_buttons, buttons, GR_M_LEFT,
308 		      GR_M_MIDDLE, GR_M_RIGHT, GR_M_P4, GR_M_P5, evaux.kbstat);
309 	mou_buttons = buttons;
310 	MOUINFO->moved = FALSE;
311 	kbd_lastmod = evaux.kbstat;
312 	return 1;
313 
314     case WM_LBUTTONUP:
315 	buttons = ~GR_M_LEFT & mou_buttons;
316 	MOUINFO->xpos = LOWORD(evaux.lParam);
317 	MOUINFO->ypos = HIWORD(evaux.lParam);
318 	fill_mouse_ev((*ev), mou_buttons, buttons, GR_M_LEFT,
319 		      GR_M_MIDDLE, GR_M_RIGHT, GR_M_P4, GR_M_P5, evaux.kbstat);
320 	mou_buttons = buttons;
321 	MOUINFO->moved = FALSE;
322 	kbd_lastmod = evaux.kbstat;
323 	return 1;
324 
325     case WM_MBUTTONUP:
326 	buttons = ~GR_M_MIDDLE & mou_buttons;
327 	MOUINFO->xpos = LOWORD(evaux.lParam);
328 	MOUINFO->ypos = HIWORD(evaux.lParam);
329 	fill_mouse_ev((*ev), mou_buttons, buttons, GR_M_LEFT,
330 		      GR_M_MIDDLE, GR_M_RIGHT, GR_M_P4, GR_M_P5, evaux.kbstat);
331 	mou_buttons = buttons;
332 	MOUINFO->moved = FALSE;
333 	kbd_lastmod = evaux.kbstat;
334 	return 1;
335 
336     case WM_RBUTTONUP:
337 	buttons = ~GR_M_RIGHT & mou_buttons;
338 	MOUINFO->xpos = LOWORD(evaux.lParam);
339 	MOUINFO->ypos = HIWORD(evaux.lParam);
340 	fill_mouse_ev((*ev), mou_buttons, buttons, GR_M_LEFT,
341 		      GR_M_MIDDLE, GR_M_RIGHT, GR_M_P4, GR_M_P5, evaux.kbstat);
342 	mou_buttons = buttons;
343 	MOUINFO->moved = FALSE;
344 	kbd_lastmod = evaux.kbstat;
345 	return 1;
346 
347     case WM_MOUSEWHEEL:
348 	buttons = mou_buttons ^ (((short)HIWORD(evaux.wParam) > 0) ?
349                                    GR_M_P4 : GR_M_P5);
350 	MOUINFO->xpos = LOWORD(evaux.lParam);
351 	MOUINFO->ypos = HIWORD(evaux.lParam);
352 	fill_mouse_ev((*ev), mou_buttons, buttons, GR_M_LEFT,
353 		      GR_M_MIDDLE, GR_M_RIGHT, GR_M_P4, GR_M_P5, evaux.kbstat);
354 	mou_buttons = buttons;
355 	MOUINFO->moved = FALSE;
356 	kbd_lastmod = evaux.kbstat;
357 	return 1;
358 
359     case WM_MOUSEMOVE:
360 	MOUINFO->xpos = LOWORD(evaux.lParam);
361 	MOUINFO->ypos = HIWORD(evaux.lParam);
362 	MOUINFO->moved = TRUE;
363         ev->kbstat = evaux.kbstat;
364 	kbd_lastmod = evaux.kbstat;
365 	return -1;
366 
367     default:
368 	return -1;
369 
370     }
371 }
372 
_GrUpdateInputs(void)373 void _GrUpdateInputs(void)
374 {
375     GrMouseEvent ev;
376     int r;
377 
378     while ((r = DequeueW32Event(&ev)) != 0) {
379 	if (r > 0) {
380             if (ev.flags & GR_M_KEYPRESS && !kbd_enabled){
381                 if (_nkeysw32pool < _MAXKEYSW32POOL)
382                     _keysw32pool[_nkeysw32pool++] = ev.key;
383             }
384             else{
385                 real_dtime(ev.dtime, evt_lasttime);
386                 enqueue_event(ev);
387                 if (ev.flags & GR_M_KEYPRESS) kbd_hitcount++;
388             }
389 	}
390     }
391 }
392